mirror of
https://github.com/TheAlgorithms/JavaScript.git
synced 2025-07-04 15:39:42 +08:00
algorithm: LCA by binary lifting (#1237)
* algorithm: LCA by binary lifting * removed trailing spaces * reduced code duplication by importing code from other file * made requested changes
This commit is contained in:
@ -9,7 +9,7 @@
|
|||||||
* Tutorial on Binary Lifting: https://codeforces.com/blog/entry/100826
|
* Tutorial on Binary Lifting: https://codeforces.com/blog/entry/100826
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class BinaryLifting {
|
export class BinaryLifting {
|
||||||
constructor (root, tree) {
|
constructor (root, tree) {
|
||||||
this.root = root
|
this.root = root
|
||||||
this.connections = new Map()
|
this.connections = new Map()
|
||||||
|
61
Graphs/LCABinaryLifting.js
Normal file
61
Graphs/LCABinaryLifting.js
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
/**
|
||||||
|
* Author: Adrito Mukherjee
|
||||||
|
* Findind Lowest Common Ancestor By Binary Lifting implementation in JavaScript
|
||||||
|
* The technique requires preprocessing the tree in O(N log N) using dynamic programming)
|
||||||
|
* It can be used to find Lowest Common Ancestor of two nodes in O(log N)
|
||||||
|
* Tutorial on Lowest Common Ancestor: https://www.geeksforgeeks.org/lca-in-a-tree-using-binary-lifting-technique
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { BinaryLifting } from './BinaryLifting'
|
||||||
|
|
||||||
|
class LCABinaryLifting extends BinaryLifting {
|
||||||
|
constructor (root, tree) {
|
||||||
|
super(root, tree)
|
||||||
|
this.depth = new Map() // depth[node] stores the depth of node from root
|
||||||
|
this.depth.set(root, 1)
|
||||||
|
this.dfsDepth(root, root)
|
||||||
|
}
|
||||||
|
|
||||||
|
dfsDepth (node, parent) {
|
||||||
|
// DFS to find depth of every node in the tree
|
||||||
|
for (const child of this.connections.get(node)) {
|
||||||
|
if (child !== parent) {
|
||||||
|
this.depth.set(child, this.depth.get(node) + 1)
|
||||||
|
this.dfsDepth(child, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getLCA (node1, node2) {
|
||||||
|
// We make sure that node1 is the deeper node among node1 and node2
|
||||||
|
if (this.depth.get(node1) < this.depth.get(node2)) {
|
||||||
|
[node1, node2] = [node2, node1]
|
||||||
|
}
|
||||||
|
// We check if node1 is the ancestor of node2, and if so, then return node1
|
||||||
|
const k = this.depth.get(node1) - this.depth.get(node2)
|
||||||
|
node1 = this.kthAncestor(node1, k)
|
||||||
|
if (node1 === node2) {
|
||||||
|
return node1
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = this.log - 1; i >= 0; i--) {
|
||||||
|
if (this.up.get(node1).get(i) !== this.up.get(node2).get(i)) {
|
||||||
|
node1 = this.up.get(node1).get(i)
|
||||||
|
node2 = this.up.get(node2).get(i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return this.up.get(node1).get(0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function lcaBinaryLifting (root, tree, queries) {
|
||||||
|
const graphObject = new LCABinaryLifting(root, tree)
|
||||||
|
const lowestCommonAncestors = []
|
||||||
|
for (const [node1, node2] of queries) {
|
||||||
|
const lca = graphObject.getLCA(node1, node2)
|
||||||
|
lowestCommonAncestors.push(lca)
|
||||||
|
}
|
||||||
|
return lowestCommonAncestors
|
||||||
|
}
|
||||||
|
|
||||||
|
export default lcaBinaryLifting
|
80
Graphs/test/LCABinaryLifting.test.js
Normal file
80
Graphs/test/LCABinaryLifting.test.js
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
import lcaBinaryLifting from '../LCABinaryLifting'
|
||||||
|
|
||||||
|
// The graph for Test Case 1 looks like this:
|
||||||
|
//
|
||||||
|
// 0
|
||||||
|
// /|\
|
||||||
|
// / | \
|
||||||
|
// 1 3 5
|
||||||
|
// / \ \
|
||||||
|
// 2 4 6
|
||||||
|
// \
|
||||||
|
// 7
|
||||||
|
// / \
|
||||||
|
// 11 8
|
||||||
|
// \
|
||||||
|
// 9
|
||||||
|
// \
|
||||||
|
// 10
|
||||||
|
|
||||||
|
test('Test case 1', () => {
|
||||||
|
const root = 0
|
||||||
|
const graph = [
|
||||||
|
[0, 1],
|
||||||
|
[0, 3],
|
||||||
|
[0, 5],
|
||||||
|
[5, 6],
|
||||||
|
[1, 2],
|
||||||
|
[1, 4],
|
||||||
|
[4, 7],
|
||||||
|
[7, 11],
|
||||||
|
[7, 8],
|
||||||
|
[8, 9],
|
||||||
|
[9, 10]
|
||||||
|
]
|
||||||
|
const queries = [
|
||||||
|
[1, 3],
|
||||||
|
[6, 5],
|
||||||
|
[3, 6],
|
||||||
|
[7, 10],
|
||||||
|
[8, 10],
|
||||||
|
[11, 2],
|
||||||
|
[11, 10]
|
||||||
|
]
|
||||||
|
const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries)
|
||||||
|
expect(lowestCommonAncestors).toEqual([0, 5, 0, 7, 8, 1, 7])
|
||||||
|
})
|
||||||
|
|
||||||
|
// The graph for Test Case 2 looks like this:
|
||||||
|
//
|
||||||
|
// 0
|
||||||
|
// / \
|
||||||
|
// 1 2
|
||||||
|
// / \ \
|
||||||
|
// 3 4 5
|
||||||
|
// / / \
|
||||||
|
// 6 7 8
|
||||||
|
|
||||||
|
test('Test case 2', () => {
|
||||||
|
const root = 0
|
||||||
|
const graph = [
|
||||||
|
[0, 1],
|
||||||
|
[0, 2],
|
||||||
|
[1, 3],
|
||||||
|
[1, 4],
|
||||||
|
[2, 5],
|
||||||
|
[3, 6],
|
||||||
|
[5, 7],
|
||||||
|
[5, 8]
|
||||||
|
]
|
||||||
|
const queries = [
|
||||||
|
[1, 2],
|
||||||
|
[3, 4],
|
||||||
|
[5, 4],
|
||||||
|
[6, 7],
|
||||||
|
[6, 8],
|
||||||
|
[7, 8]
|
||||||
|
]
|
||||||
|
const lowestCommonAncestors = lcaBinaryLifting(root, graph, queries)
|
||||||
|
expect(lowestCommonAncestors).toEqual([0, 1, 0, 0, 0, 5])
|
||||||
|
})
|
Reference in New Issue
Block a user