Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add Lowest Common Ancestor algorithm #258

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions src/algorithms/tree/lowest-common-ancestor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Lowest common ancestor(LCA) algorithm

In graph theory and computer science, the lowest common ancestor (LCA) of two nodes v and w in a tree or directed acyclic graph (DAG) T is the lowest (i.e. deepest) node that has both v and w as descendants, where we define each node to be a descendant of itself (so if v has a direct connection from w, w is the lowest common ancestor).

The LCA of v and w in T is the shared ancestor of v and w that is located farthest from the root. Computation of lowest common ancestors may be useful, for instance, as part of a procedure for determining the distance between pairs of nodes in a tree: the distance from v to w can be computed as the distance from the root to v, plus the distance from the root to w, minus twice the distance from the root to their lowest common ancestor (Djidjev, Pantziou & Zaroliagis 1991). In ontologies, the lowest common ancestor is also known as the least common subsumer.

In a tree data structure where each node points to its parent, the lowest common ancestor can be easily determined by finding the first intersection of the paths from v and w to the root. In general, the computational time required for this algorithm is O(h) where h is the height of the tree (length of longest path from a leaf to the root). However, there exist several algorithms for processing trees so that lowest common ancestors may be found more quickly. Tarjan's off-line lowest common ancestors algorithm, for example, preprocesses a tree in linear time to provide constant-time LCA queries. In general DAGs, similar algorithms exist, but with super-linear complexity.


## References

- [Wikipedia](https://en.wikipedia.org/wiki/Lowest_common_ancestor) (https://en.wikipedia.org/wiki/Lowest_common_ancestor)

55 changes: 55 additions & 0 deletions src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* @typedef {Object} Callbacks
* @property {function(node: BinaryTreeNode, child: BinaryTreeNode): boolean} allowTraversal -
* Determines whether DFS should traverse from the node to its child.
*/

/**
* @param {Callbacks} [callbacks]
* @returns {Callbacks}
*/

/**
* @param {BinaryTreeNode} rootNode
* @param {Callbacks} [originalCallbacks]
*/
function calcDepth(node) {
let depth = 0;
let tempNode = null;
tempNode = node;
while (tempNode.parent == null) {
tempNode = tempNode.parent;
depth += 1;
}

return depth;
}

/* lowest common ancestor */
export default function lca(rootNode, firstNode, secondNode) {
const firstDepth = calcDepth(firstNode);
const secondDepth = calcDepth(secondNode);
let firstOne = null;
let secondOne = null;
firstOne = firstNode;
secondOne = secondNode;

for (let i = 0; i < Math.abs(firstDepth - secondDepth); i += 1) {
if (firstDepth > secondDepth) {
firstOne = firstOne.parent;
} else {
secondOne = secondOne.parent;
}
}

if (firstNode === secondNode) {
return firstOne;
}

while (firstOne !== secondOne) {
firstOne = firstOne.parent;
secondOne = secondOne.parent;
}

return firstOne;
}