Skip to content

Commit

Permalink
feat: Lowest Common Ancestor (LCA) algorithm (TheAlgorithms#512)
Browse files Browse the repository at this point in the history
  • Loading branch information
datbeohbbh authored Jul 7, 2022
1 parent 3a41a76 commit d05e013
Show file tree
Hide file tree
Showing 3 changed files with 418 additions and 37 deletions.
84 changes: 47 additions & 37 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,12 +93,13 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
2. [`BitCounter`](./math/binary/bitcounter.go#L11): BitCounter - The function returns the number of set bits for an unsigned integer number
3. [`IsPowerOfTwo`](./math/binary/checkisnumberpoweroftwo.go#L19): IsPowerOfTwo This function uses the fact that powers of 2 are represented like 10...0 in binary, and numbers one less than the power of 2 are represented like 11...1. Therefore, using the and function: 10...0 & 01...1 00...0 -> 0 This is also true for 0, which is not a power of 2, for which we have to add and extra condition.
4. [`IsPowerOfTwoLeftShift`](./math/binary/checkisnumberpoweroftwo.go#L26): IsPowerOfTwoLeftShift This function takes advantage of the fact that left shifting a number by 1 is equivalent to multiplying by 2. For example, binary 00000001 when shifted by 3 becomes 00001000, which in decimal system is 8 or = 2 * 2 * 2
5. [`MeanUsingAndXor`](./math/binary/arithmeticmean.go#L12): MeanUsingAndXor This function finds arithmetic mean using "AND" and "XOR" operations
6. [`MeanUsingRightShift`](./math/binary/arithmeticmean.go#L17): MeanUsingRightShift This function finds arithmetic mean using right shift
7. [`ReverseBits`](./math/binary/reversebits.go#L14): ReverseBits This function initialized the result by 0 (all bits 0) and process the given number starting from its least significant bit. If the current bit is 1, set the corresponding most significant bit in the result and finally move on to the next bit in the input number. Repeat this till all its bits are processed.
8. [`SequenceGrayCode`](./math/binary/rbc.go#L11): SequenceGrayCode The function generates an "Gray code" sequence of length n
9. [`Sqrt`](./math/binary/sqrt.go#L16): No description provided.
10. [`XorSearchMissingNumber`](./math/binary/xorsearch.go#L11): XorSearchMissingNumber This function finds a missing number in a sequence
5. [`LogBase2`](./math/binary/logarithm.go#L7): LogBase2 Finding the exponent of n = 2**x using bitwise operations (logarithm in base 2 of n) [See more](https://en.wikipedia.org/wiki/Logarithm)
6. [`MeanUsingAndXor`](./math/binary/arithmeticmean.go#L12): MeanUsingAndXor This function finds arithmetic mean using "AND" and "XOR" operations
7. [`MeanUsingRightShift`](./math/binary/arithmeticmean.go#L17): MeanUsingRightShift This function finds arithmetic mean using right shift
8. [`ReverseBits`](./math/binary/reversebits.go#L14): ReverseBits This function initialized the result by 0 (all bits 0) and process the given number starting from its least significant bit. If the current bit is 1, set the corresponding most significant bit in the result and finally move on to the next bit in the input number. Repeat this till all its bits are processed.
9. [`SequenceGrayCode`](./math/binary/rbc.go#L11): SequenceGrayCode The function generates an "Gray code" sequence of length n
10. [`Sqrt`](./math/binary/sqrt.go#L16): No description provided.
11. [`XorSearchMissingNumber`](./math/binary/xorsearch.go#L11): XorSearchMissingNumber This function finds a missing number in a sequence

---
</details><details>
Expand Down Expand Up @@ -256,24 +257,25 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
---
##### Functions:

1. [`Bin2`](./dynamic/binomialcoefficient.go#L21): Bin2 function
2. [`CoinChange`](./dynamic/coinchange.go#L5): CoinChange finds the number of possible combinations of coins of different values which can get to the target amount.
3. [`CutRodDp`](./dynamic/rodcutting.go#L21): CutRodDp solve the same problem using dynamic programming
4. [`CutRodRec`](./dynamic/rodcutting.go#L8): CutRodRec solve the problem recursively: initial approach
5. [`EditDistanceDP`](./dynamic/editdistance.go#L35): EditDistanceDP is an optimised implementation which builds on the ideas of the recursive implementation. We use dynamic programming to compute the DP table where dp[i][j] denotes the edit distance value of first[0..i-1] and second[0..j-1]. Time complexity is O(m * n) where m and n are lengths of the strings, first and second respectively.
6. [`EditDistanceRecursive`](./dynamic/editdistance.go#L10): EditDistanceRecursive is a naive implementation with exponential time complexity.
7. [`IsSubsetSum`](./dynamic/subsetsum.go#L14): No description provided.
8. [`Knapsack`](./dynamic/knapsack.go#L17): Knapsack solves knapsack problem return maxProfit
9. [`LongestCommonSubsequence`](./dynamic/longestcommonsubsequence.go#L8): LongestCommonSubsequence function
10. [`LongestIncreasingSubsequence`](./dynamic/longestincreasingsubsequence.go#L9): LongestIncreasingSubsequence returns the longest increasing subsequence where all elements of the subsequence are sorted in increasing order
11. [`LongestIncreasingSubsequenceGreedy`](./dynamic/longestincreasingsubsequencegreedy.go#L9): LongestIncreasingSubsequenceGreedy is a function to find the longest increasing subsequence in a given array using a greedy approach. The dynamic programming approach is implemented alongside this one. Worst Case Time Complexity: O(nlogn) Auxiliary Space: O(n), where n is the length of the array(slice). Reference: https://www.geeksforgeeks.org/construction-of-longest-monotonically-increasing-subsequence-n-log-n/
12. [`LpsDp`](./dynamic/longestpalindromicsubsequence.go#L21): LpsDp function
13. [`LpsRec`](./dynamic/longestpalindromicsubsequence.go#L7): LpsRec function
14. [`MatrixChainDp`](./dynamic/matrixmultiplication.go#L24): MatrixChainDp function
15. [`MatrixChainRec`](./dynamic/matrixmultiplication.go#L10): MatrixChainRec function
16. [`Max`](./dynamic/knapsack.go#L11): Max function - possible duplicate
17. [`NthCatalanNumber`](./dynamic/catalan.go#L13): NthCatalan returns the n-th Catalan Number Complexity: O(n²)
18. [`NthFibonacci`](./dynamic/fibonacci.go#L6): NthFibonacci returns the nth Fibonacci Number
1. [`Abbreviation`](./dynamic/abbreviation.go#L24): Returns true if it is possible to make a equals b (if b is an abbreviation of a), returns false otherwise
2. [`Bin2`](./dynamic/binomialcoefficient.go#L21): Bin2 function
3. [`CoinChange`](./dynamic/coinchange.go#L5): CoinChange finds the number of possible combinations of coins of different values which can get to the target amount.
4. [`CutRodDp`](./dynamic/rodcutting.go#L21): CutRodDp solve the same problem using dynamic programming
5. [`CutRodRec`](./dynamic/rodcutting.go#L8): CutRodRec solve the problem recursively: initial approach
6. [`EditDistanceDP`](./dynamic/editdistance.go#L35): EditDistanceDP is an optimised implementation which builds on the ideas of the recursive implementation. We use dynamic programming to compute the DP table where dp[i][j] denotes the edit distance value of first[0..i-1] and second[0..j-1]. Time complexity is O(m * n) where m and n are lengths of the strings, first and second respectively.
7. [`EditDistanceRecursive`](./dynamic/editdistance.go#L10): EditDistanceRecursive is a naive implementation with exponential time complexity.
8. [`IsSubsetSum`](./dynamic/subsetsum.go#L14): No description provided.
9. [`Knapsack`](./dynamic/knapsack.go#L17): Knapsack solves knapsack problem return maxProfit
10. [`LongestCommonSubsequence`](./dynamic/longestcommonsubsequence.go#L8): LongestCommonSubsequence function
11. [`LongestIncreasingSubsequence`](./dynamic/longestincreasingsubsequence.go#L9): LongestIncreasingSubsequence returns the longest increasing subsequence where all elements of the subsequence are sorted in increasing order
12. [`LongestIncreasingSubsequenceGreedy`](./dynamic/longestincreasingsubsequencegreedy.go#L9): LongestIncreasingSubsequenceGreedy is a function to find the longest increasing subsequence in a given array using a greedy approach. The dynamic programming approach is implemented alongside this one. Worst Case Time Complexity: O(nlogn) Auxiliary Space: O(n), where n is the length of the array(slice). Reference: https://www.geeksforgeeks.org/construction-of-longest-monotonically-increasing-subsequence-n-log-n/
13. [`LpsDp`](./dynamic/longestpalindromicsubsequence.go#L21): LpsDp function
14. [`LpsRec`](./dynamic/longestpalindromicsubsequence.go#L7): LpsRec function
15. [`MatrixChainDp`](./dynamic/matrixmultiplication.go#L24): MatrixChainDp function
16. [`MatrixChainRec`](./dynamic/matrixmultiplication.go#L10): MatrixChainRec function
17. [`Max`](./dynamic/knapsack.go#L11): Max function - possible duplicate
18. [`NthCatalanNumber`](./dynamic/catalan.go#L13): NthCatalan returns the n-th Catalan Number Complexity: O(n²)
19. [`NthFibonacci`](./dynamic/fibonacci.go#L6): NthFibonacci returns the nth Fibonacci Number

---
</details><details>
Expand Down Expand Up @@ -411,10 +413,12 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
5. [`FloydWarshall`](./graph/floydwarshall.go#L15): FloydWarshall Returns all pair's shortest path using Floyd Warshall algorithm
6. [`GetIdx`](./graph/depthfirstsearch.go#L3): No description provided.
7. [`KruskalMST`](./graph/kruskal.go#L87): KruskalMST will return a minimum spanning tree along with its total cost to using Kruskal's algorithm. Time complexity is O(m * log (n)) where m is the number of edges in the graph and n is number of nodes in it.
8. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
9. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made
10. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
11. [`Topological`](./graph/topological.go#L7): Assumes that graph given is valid and possible to get a topo ordering. constraints are array of []int{a, b}, representing an edge going from a to b
8. [`LowestCommonAncestor`](./graph/lowestcommonancestor.go#L111): For each node, we will precompute its ancestor above him, its ancestor two nodes above, its ancestor four nodes above, etc. Let's call `jump[j][u]` is the `2^j`-th ancestor above the node `u` with `u` in range `[0, numbersVertex)`, `j` in range `[0,MAXLOG)`. These information allow us to jump from any node to any ancestor above it in `O(MAXLOG)` time.
9. [`New`](./graph/graph.go#L16): Constructor functions for graphs (undirected by default)
10. [`NewDSU`](./graph/kruskal.go#L34): NewDSU will return an initialised DSU using the value of n which will be treated as the number of elements out of which the DSU is being made
11. [`NewTree`](./graph/lowestcommonancestor.go#L84): No description provided.
12. [`NotExist`](./graph/depthfirstsearch.go#L12): No description provided.
13. [`Topological`](./graph/topological.go#L7): Assumes that graph given is valid and possible to get a topo ordering. constraints are array of []int{a, b}, representing an edge going from a to b

---
##### Types
Expand All @@ -429,7 +433,13 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.

5. [`Item`](./graph/dijkstra.go#L5): No description provided.

6. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided.
6. [`Query`](./graph/lowestcommonancestor_test.go#L9): No description provided.

7. [`Tree`](./graph/lowestcommonancestor.go#L25): No description provided.

8. [`TreeEdge`](./graph/lowestcommonancestor.go#L12): No description provided.

9. [`WeightedGraph`](./graph/floydwarshall.go#L9): No description provided.


---
Expand Down Expand Up @@ -738,14 +748,14 @@ Read our [Contribution Guidelines](CONTRIBUTING.md) before you contribute.
1. [`Factorize`](./math/prime/primefactorization.go#L5): Factorize is a function that computes the exponents of each prime in the prime factorization of n
2. [`Generate`](./math/prime/sieve.go#L26): Generate returns a int slice of prime numbers up to the limit
3. [`GenerateChannel`](./math/prime/sieve.go#L9): Generate generates the sequence of integers starting at 2 and sends it to the channel `ch`
4. [`MillerRabinDeterministic`](./math/prime/millerrabinprimalitytest.go#L121): MillerRabinDeterministic is a Deterministic version of the Miller-Rabin test, which returns correct results for all valid int64 numbers.
5. [`MillerRabinProbabilistic`](./math/prime/millerrabinprimalitytest.go#L101): MillerRabinProbabilistic is a probabilistic test for primality of an integer based of the algorithm devised by Miller and Rabin.
6. [`MillerRandomTest`](./math/prime/millerrabinprimalitytest.go#L77): MillerRandomTest This is the intermediate step that repeats within the miller rabin primality test for better probabilitic chances of receiving the correct result with random witnesses.
7. [`MillerTest`](./math/prime/millerrabinprimalitytest.go#L49): MillerTest tests whether num is a strong probable prime to a witness. Formally: a^d ≡ 1 (mod n) or a^(2^r * d) ≡ -1 (mod n), 0 <= r <= s
8. [`MillerTestMultiple`](./math/prime/millerrabinprimalitytest.go#L84): MillerTestMultiple is like MillerTest but runs the test for multiple witnesses.
9. [`NaiveApproach`](./math/prime/primecheck.go#L8): NaiveApproach checks if an integer is prime or not. Returns a bool.
10. [`PairApproach`](./math/prime/primecheck.go#L22): PairApproach checks primality of an integer and returns a bool. More efficient than the naive approach as number of iterations are less.
11. [`Sieve`](./math/prime/sieve.go#L16): Sieve Sieving the numbers that are not prime from the channel - basically removing them from the channels
4. [`MillerRabinDeterministic`](./math/prime/millerrabintest.go#L121): MillerRabinDeterministic is a Deterministic version of the Miller-Rabin test, which returns correct results for all valid int64 numbers.
5. [`MillerRabinProbabilistic`](./math/prime/millerrabintest.go#L101): MillerRabinProbabilistic is a probabilistic test for primality of an integer based of the algorithm devised by Miller and Rabin.
6. [`MillerRandomTest`](./math/prime/millerrabintest.go#L77): MillerRandomTest This is the intermediate step that repeats within the miller rabin primality test for better probabilitic chances of receiving the correct result with random witnesses.
7. [`MillerTest`](./math/prime/millerrabintest.go#L49): MillerTest tests whether num is a strong probable prime to a witness. Formally: a^d ≡ 1 (mod n) or a^(2^r * d) ≡ -1 (mod n), 0 <= r <= s
8. [`MillerTestMultiple`](./math/prime/millerrabintest.go#L84): MillerTestMultiple is like MillerTest but runs the test for multiple witnesses.
9. [`OptimizedTrialDivision`](./math/prime/primecheck.go#L26): OptimizedTrialDivision checks primality of an integer using an optimized trial division method. The optimizations include not checking divisibility by the even numbers and only checking up to the square root of the given number.
10. [`Sieve`](./math/prime/sieve.go#L16): Sieve Sieving the numbers that are not prime from the channel - basically removing them from the channels
11. [`TrialDivision`](./math/prime/primecheck.go#L9): TrialDivision tests whether a number is prime by trying to divide it by the numbers less than it.

---
</details><details>
Expand Down
121 changes: 121 additions & 0 deletions graph/lowestcommonancestor.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
// lowestcommonancestor.go
// description: Implementation of Lowest common ancestor (LCA) algorithm.
// detail:
// Let `T` be a tree. The LCA of `u` and `v` in T is the shared ancestor of `u` and `v`
// that is located farthest from the root.
// references: [cp-algorithms](https://cp-algorithms.com/graph/lca_binary_lifting.html)
// author(s) [Dat](https://github.com/datbeohbbh)
// see lowestcommonancestor_test.go for a test implementation.

package graph

type TreeEdge struct {
from int
to int
}

type ITree interface {
dfs(int, int)
addEdge(int, int)
GetDepth(int) int
GetDad(int) int
GetLCA(int, int) int
}

type Tree struct {
numbersVertex int
root int
MAXLOG int
depth []int
dad []int
jump [][]int
edges [][]int
}

func (tree *Tree) addEdge(u, v int) {
tree.edges[u] = append(tree.edges[u], v)
tree.edges[v] = append(tree.edges[v], u)
}

func (tree *Tree) dfs(u, par int) {
tree.jump[0][u] = par
tree.dad[u] = par
for _, v := range tree.edges[u] {
if v != par {
tree.depth[v] = tree.depth[u] + 1
tree.dfs(v, u)
}
}
}

func (tree *Tree) GetDepth(u int) int {
return tree.depth[u]
}

func (tree *Tree) GetDad(u int) int {
return tree.dad[u]
}

func (tree *Tree) GetLCA(u, v int) int {
if tree.GetDepth(u) < tree.GetDepth(v) {
u, v = v, u
}

for j := tree.MAXLOG - 1; j >= 0; j-- {
if tree.GetDepth(tree.jump[j][u]) >= tree.GetDepth(v) {
u = tree.jump[j][u]
}
}

if u == v {
return u
}

for j := tree.MAXLOG - 1; j >= 0; j-- {
if tree.jump[j][u] != tree.jump[j][v] {
u = tree.jump[j][u]
v = tree.jump[j][v]
}
}

return tree.jump[0][u]
}

func NewTree(numbersVertex, root int, edges []TreeEdge) (tree *Tree) {
tree = new(Tree)
tree.numbersVertex, tree.root, tree.MAXLOG = numbersVertex, root, 0
tree.depth = make([]int, numbersVertex)
tree.dad = make([]int, numbersVertex)

for (1 << tree.MAXLOG) <= numbersVertex {
(tree.MAXLOG) += 1
}
(tree.MAXLOG) += 1

tree.jump = make([][]int, tree.MAXLOG)
for j := 0; j < tree.MAXLOG; j++ {
tree.jump[j] = make([]int, numbersVertex)
}

tree.edges = make([][]int, numbersVertex)
for _, e := range edges {
tree.addEdge(e.from, e.to)
}

return tree
}

// For each node, we will precompute its ancestor above him, its ancestor two nodes above, its ancestor four nodes above, etc.
// Let's call `jump[j][u]` is the `2^j`-th ancestor above the node `u` with `u` in range `[0, numbersVertex)`, `j` in range `[0,MAXLOG)`.
// These information allow us to jump from any node to any ancestor above it in `O(MAXLOG)` time.
func LowestCommonAncestor(tree *Tree) {
// call dfs to compute depth from the root to each node and the parent of each node.
tree.dfs(tree.root, tree.root)

// compute jump[j][u]
for j := 1; j < tree.MAXLOG; j++ {
for u := 0; u < tree.numbersVertex; u++ {
tree.jump[j][u] = tree.jump[j-1][tree.jump[j-1][u]]
}
}
}
Loading

0 comments on commit d05e013

Please sign in to comment.