From aba089c9faf66bd43779d074749ed4e46bf5f5c4 Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 12:31:23 +0900 Subject: [PATCH 1/8] add Lowest Common Ancestor algorithm --- .../tree/lowest-common-ancestor/README.md | 13 +++++ .../lowestCommonAncestor.js | 48 +++++++++++++++++++ 2 files changed, 61 insertions(+) create mode 100644 src/algorithms/tree/lowest-common-ancestor/README.md create mode 100644 src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js diff --git a/src/algorithms/tree/lowest-common-ancestor/README.md b/src/algorithms/tree/lowest-common-ancestor/README.md new file mode 100644 index 00000000..0d181b7d --- /dev/null +++ b/src/algorithms/tree/lowest-common-ancestor/README.md @@ -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) + diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js new file mode 100644 index 00000000..d5020309 --- /dev/null +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -0,0 +1,48 @@ +/** + * @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 = var function(node){ + let depth = 0; + while (node.parent == null) { + node = node.parent; + depth += 1; + } + + return depth; +} + +/*lowest common ancestor*/ +export default function lca(rootNode, firstNode, secondNode) { + const firstDepth = calcDepth(firstNode); + const secondDepth = calcDepth(secondNode); + + for (int i = 0; i < Math.abs(firstDepth - secondDepth); i++) { + if (firstDepth > secondDepth) + firstNode = firstNode.parent; + else + secondNode = secondNode.parent; + } + + if (firstNode == secondNode) + resultNode = firstNode; + + while (firstNode != secondNode) { + firstNode = firstNode.parent; + secondNode = secondNode.parent; + } + + return firstNode; +} + From dbee69fa3deac6ca2fc1c3b6094e02835237696b Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 12:35:25 +0900 Subject: [PATCH 2/8] update lowestCommonAncestor.js --- .../tree/lowest-common-ancestor/lowestCommonAncestor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index d5020309..65d5c5c8 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -13,7 +13,7 @@ * @param {BinaryTreeNode} rootNode * @param {Callbacks} [originalCallbacks] */ -function calcDepth = var function(node){ +function calcDepth(node){ let depth = 0; while (node.parent == null) { node = node.parent; From 5c6b4f23e42f7676fa8642e284e658a42c63721e Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 12:42:31 +0900 Subject: [PATCH 3/8] updata lowest common ancestor.js --- .../tree/lowest-common-ancestor/lowestCommonAncestor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index 65d5c5c8..c3a5b095 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -28,7 +28,7 @@ export default function lca(rootNode, firstNode, secondNode) { const firstDepth = calcDepth(firstNode); const secondDepth = calcDepth(secondNode); - for (int i = 0; i < Math.abs(firstDepth - secondDepth); i++) { + for (let i = 0; i < Math.abs(firstDepth - secondDepth); i++) { if (firstDepth > secondDepth) firstNode = firstNode.parent; else From 643d6b21a78b996b0ce9e500ebff01ce4bcddd4d Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 13:40:29 +0900 Subject: [PATCH 4/8] update lower common ancestor js --- .../lowestCommonAncestor.js | 53 ++++++++++--------- 1 file changed, 28 insertions(+), 25 deletions(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index c3a5b095..db3b1178 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -13,36 +13,39 @@ * @param {BinaryTreeNode} rootNode * @param {Callbacks} [originalCallbacks] */ -function calcDepth(node){ - let depth = 0; - while (node.parent == null) { - node = node.parent; - depth += 1; - } +function calcDepth = var function(node) { + let depth = 0; + while (node.parent == null) { + node = node.parent; + depth += 1; + } - return depth; + return depth; } -/*lowest common ancestor*/ +/* lowest common ancestor */ export default function lca(rootNode, firstNode, secondNode) { - const firstDepth = calcDepth(firstNode); - const secondDepth = calcDepth(secondNode); - - for (let i = 0; i < Math.abs(firstDepth - secondDepth); i++) { - if (firstDepth > secondDepth) - firstNode = firstNode.parent; - else - secondNode = secondNode.parent; - } - - if (firstNode == secondNode) - resultNode = firstNode; + const firstDepth = calcDepth(firstNode); + const secondDepth = calcDepth(secondNode); + let firstOne = firstNode; + let secondOne = secondNode; + + for (let i = 0; i < Math.abs(firstDepth - secondDepth); i = i + 1) { + if (firstDepth > secondDepth) { + firstOne = firstOne.parent; + } else { + secondOne = secondOne.parent; + } + } - while (firstNode != secondNode) { - firstNode = firstNode.parent; - secondNode = secondNode.parent; - } + if (firstNode === secondNode) + return firstOne; - return firstNode; + while (firstOne !== secondOne) { + firstOne = firstOne.parent; + secondOne = secondOne.parent; + } + + return firstOne; } From 07602b9973bf566a588662f7b6cc7c3ca069e816 Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 13:51:09 +0900 Subject: [PATCH 5/8] update LCA algorithm --- .../tree/lowest-common-ancestor/lowestCommonAncestor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index db3b1178..f601e3d8 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -13,7 +13,7 @@ * @param {BinaryTreeNode} rootNode * @param {Callbacks} [originalCallbacks] */ -function calcDepth = var function(node) { +function calcDepth(node) { let depth = 0; while (node.parent == null) { node = node.parent; From ee00302873a4462084155346d6bff4299fb6d188 Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 14:03:18 +0900 Subject: [PATCH 6/8] update LCA algorithm --- .../lowestCommonAncestor.js | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index f601e3d8..952771d6 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -15,8 +15,10 @@ */ function calcDepth(node) { let depth = 0; - while (node.parent == null) { - node = node.parent; + let tempNode = null; + tempNode = node; + while (tempNode.parent == null) { + tempNode = tempNode.parent; depth += 1; } @@ -27,9 +29,11 @@ function calcDepth(node) { export default function lca(rootNode, firstNode, secondNode) { const firstDepth = calcDepth(firstNode); const secondDepth = calcDepth(secondNode); - let firstOne = firstNode; - let secondOne = secondNode; - + let firstOne = null; + let secondOne = null; + firstOne = firstNode; + secondOne = secondNode; + for (let i = 0; i < Math.abs(firstDepth - secondDepth); i = i + 1) { if (firstDepth > secondDepth) { firstOne = firstOne.parent; @@ -38,8 +42,9 @@ export default function lca(rootNode, firstNode, secondNode) { } } - if (firstNode === secondNode) + if (firstNode === secondNode) { return firstOne; + } while (firstOne !== secondOne) { firstOne = firstOne.parent; From 72f49d88fafa839eac1500cfd1b4a8fbe9d48642 Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 14:26:24 +0900 Subject: [PATCH 7/8] update LCA algorithm --- .../tree/lowest-common-ancestor/lowestCommonAncestor.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index 952771d6..c0203d41 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -16,7 +16,7 @@ function calcDepth(node) { let depth = 0; let tempNode = null; - tempNode = node; + tempNode = node; while (tempNode.parent == null) { tempNode = tempNode.parent; depth += 1; @@ -34,7 +34,7 @@ export default function lca(rootNode, firstNode, secondNode) { firstOne = firstNode; secondOne = secondNode; - for (let i = 0; i < Math.abs(firstDepth - secondDepth); i = i + 1) { + for (let i = 0; i < Math.abs(firstDepth - secondDepth); i += 1) { if (firstDepth > secondDepth) { firstOne = firstOne.parent; } else { From 17f3b8f2458727b0426400307695776a0f1a2836 Mon Sep 17 00:00:00 2001 From: orist22 Date: Wed, 5 Dec 2018 14:30:11 +0900 Subject: [PATCH 8/8] update LCA algorithms --- .../tree/lowest-common-ancestor/lowestCommonAncestor.js | 1 - 1 file changed, 1 deletion(-) diff --git a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js index c0203d41..30567036 100644 --- a/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js +++ b/src/algorithms/tree/lowest-common-ancestor/lowestCommonAncestor.js @@ -53,4 +53,3 @@ export default function lca(rootNode, firstNode, secondNode) { return firstOne; } -