Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 259c452

Browse files
committedOct 20, 2022
Algorithm: BinaryLifting
1 parent ce9e294 commit 259c452

File tree

2 files changed

+181
-0
lines changed

2 files changed

+181
-0
lines changed
 

‎Graphs/BinaryLifting.js

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
/**
2+
* Author: Adrito Mukherjee
3+
* Binary Lifting implementation in Javascript
4+
* Binary Lifting is a technique that is used to find the kth ancestor of a node in a rooted tree with N nodes
5+
* The technique requires preprocessing the tree in O(N log N) using dynamic programming
6+
* The techniqe can answer Q queries about kth ancestor of any node in O(Q log N)
7+
* It is faster than the naive algorithm that answers Q queries with complexity O(Q K)
8+
* It can be used to find Lowest Common Ancestor of two nodes in O(log N)
9+
*/
10+
11+
class BinaryLifting {
12+
constructor (root, tree) {
13+
this.connections = {}
14+
this.up = {} // up[node][i] stores the 2^i-th parent of node
15+
for (const [i, j] of tree) {
16+
this.addEdge(i, j)
17+
}
18+
// LOG should be such that 2^LOG is greater than total number of nodes in the tree
19+
this.LOG = 0
20+
while ((1 << this.LOG) <= Object.keys(this.connections).length) {
21+
this.LOG++
22+
}
23+
this.dfs(root, root)
24+
}
25+
26+
addNode (node) {
27+
// Function to add a node to the tree (connection represented by set)
28+
this.connections[node] = new Set()
29+
}
30+
31+
addEdge (node1, node2) {
32+
// Function to add an edge (adds the node too if they are not present in the tree)
33+
if (!(node1 in this.connections)) {
34+
this.addNode(node1)
35+
}
36+
if (!(node2 in this.connections)) {
37+
this.addNode(node2)
38+
}
39+
this.connections[node1].add(node2)
40+
this.connections[node2].add(node1)
41+
}
42+
43+
dfs (node, parent) {
44+
this.up[node] = {}
45+
this.up[node][0] = parent
46+
for (let i = 1; i < this.LOG; i++) {
47+
this.up[node][i] = this.up[this.up[node][i - 1]][i - 1]
48+
}
49+
for (const child of this.connections[node]) {
50+
if (child !== parent) this.dfs(child, node)
51+
}
52+
}
53+
54+
kthAncestor (node, k) {
55+
for (let i = 0; i < this.LOG; i++) {
56+
if (k & (1 << i)) {
57+
node = this.up[node][i]
58+
}
59+
}
60+
return node
61+
}
62+
}
63+
64+
function binaryLifting (root, tree, queries) {
65+
const graphObject = new BinaryLifting(root, tree)
66+
const ancestors = []
67+
for (const [node, k] of queries) {
68+
const ancestor = graphObject.kthAncestor(node, k)
69+
ancestors.push(ancestor)
70+
}
71+
return ancestors
72+
}
73+
74+
export { binaryLifting }
75+
76+
// binaryLifting(
77+
// 0,
78+
// [
79+
// [0, 1],
80+
// [0, 3],
81+
// [0, 5],
82+
// [5, 6],
83+
// [1, 2],
84+
// [1, 4],
85+
// [4, 7],
86+
// [7, 11],
87+
// [7, 8],
88+
// [8, 9],
89+
// [9, 10]
90+
// ],
91+
// [
92+
// [10, 4],
93+
// [10, 7],
94+
// [7, 2],
95+
// [11, 3]
96+
// ]
97+
// )
98+
99+
// [4, 0, 1, 1]

‎Graphs/test/BinaryLifting.test.js

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { binaryLifting } from '../BinaryLifting'
2+
3+
// The graph for Test Case 1 looks like this:
4+
//
5+
// 0
6+
// /|\
7+
// / | \
8+
// 1 3 5
9+
// / \ \
10+
// 2 4 6
11+
// \
12+
// 7
13+
// / \
14+
// 11 8
15+
// \
16+
// 9
17+
// \
18+
// 10
19+
20+
test('Test case 1', () => {
21+
const root = 0
22+
const graph = [
23+
[0, 1],
24+
[0, 3],
25+
[0, 5],
26+
[5, 6],
27+
[1, 2],
28+
[1, 4],
29+
[4, 7],
30+
[7, 11],
31+
[7, 8],
32+
[8, 9],
33+
[9, 10]
34+
]
35+
const queries = [
36+
[2, 1],
37+
[6, 1],
38+
[7, 2],
39+
[8, 2],
40+
[10, 2],
41+
[10, 3],
42+
[10, 5],
43+
[11, 3]
44+
]
45+
const kthAncestors = binaryLifting(root, graph, queries)
46+
expect(kthAncestors).toEqual([1, 5, 1, 4, 8, 7, 1, 1])
47+
})
48+
49+
// The graph for Test Case 2 looks like this:
50+
//
51+
// 0
52+
// / \
53+
// 1 2
54+
// / \ \
55+
// 3 4 5
56+
// / / \
57+
// 6 7 8
58+
59+
test('Test case 2', () => {
60+
const root = 0
61+
const graph = [
62+
[0, 1],
63+
[0, 2],
64+
[1, 3],
65+
[1, 4],
66+
[2, 5],
67+
[3, 6],
68+
[5, 7],
69+
[5, 8]
70+
]
71+
const queries = [
72+
[2, 1],
73+
[3, 1],
74+
[3, 2],
75+
[6, 2],
76+
[7, 3],
77+
[8, 2],
78+
[8, 3]
79+
]
80+
const kthAncestors = binaryLifting(root, graph, queries)
81+
expect(kthAncestors).toEqual([0, 1, 0, 1, 0, 2, 0])
82+
})

0 commit comments

Comments
 (0)
Please sign in to comment.