From 36de7f250714ac13c65bb8ffaa2e91b483e7a568 Mon Sep 17 00:00:00 2001 From: nethish Date: Thu, 30 Jan 2020 13:19:12 +0530 Subject: [PATCH 01/13] Added LCA and tests --- pydatastructs/trees/binary_trees.py | 101 ++++++++++++++++++ .../trees/tests/test_binary_trees.py | 7 ++ 2 files changed, 108 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 91fb96528..c195aaecd 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -415,6 +415,107 @@ def rank(self, x): walk = p return r + def _simple_path(self, key, root, path): + """ + Utility funtion to find the simple path between root and node. + """ + if root is None: + return False + path.append(root) + if self.tree[root].key == key: + return True + + if self._simple_path(key, self.tree[root].left, path) or \ + self._simple_path(key, self.tree[root].right, path): + path.append(root) + return True + + path.pop() + return False + + def simple_path(self, key): + path = [] + self._simple_path(key, self.root_idx, path) + return path + + def lowest_common_ancestor(self, j, k, algorithm=1): + + """ + Computes the lowest common ancestor of two nodes. + + Parameters + ========== + + j: Node.key + key of first node + k: Node.key + key of second node + algorithm: int + The algorithm to be used for computing the + lowest common ancestor. + Optional, by default uses algorithm 1. + + 1 -> Determines the lowest common ancestor by finding + the first intersection of the paths from v and w + to the root. + + 2 -> Modifed version of the algorithm given in the + following publication, + D. Harel. A linear time algorithm for the + lowest common ancestors problem. In 21s + Annual Symposium On Foundations of + Computer Science, pages 308-319, 1980. + + Returns + ======= + + int + The index of the lowest common ancestor in the tree. + if both the nodes are present in the tree. + None + In all other cases. + + References + ========== + + .. [1] https://en.wikipedia.org/wiki/Lowest_common_ancestor + + .. [2] https://pdfs.semanticscholar.org/e75b/386cc554214aa0ebd6bd6dbdd0e490da3739.pdf + + """ + if algorithm == 1: + curr_root = self.root_idx + u, v = self.search(j), self.search(k) + if (u is None) or (v is None): + return None + u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) + while not (u_left ^ v_left): + if u_left and v_left: + curr_root = self.tree[curr_root].left + else: + curr_root = self.tree[curr_root].right + if curr_root == u or curr_root == v: + return curr_root + u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) + return curr_root + else: + root = self.root_idx + path1 = self.simple_path(j) + path2 = self.simple_path(k) + if not path1 or not path2: + return None + + n, m = len(path1), len(path2) + i = j = 0 + while i < n and j < m: + if path1[i] != path2[j]: + return path1[i - 1] + i += 1 + j += 1 + return None + class AVLTree(BinarySearchTree): """ Represents AVL trees. diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 15bb2f79a..f0bec4eb8 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -54,6 +54,13 @@ def test_BinarySearchTree(): assert b.delete(-10) is True assert b.delete(-3) is True assert b.delete(-13) is None + bl = BST() + nodes = [50, 30, 90, 70, 100, 60, 80, 55] + for node in nodes: + bl.insert(node, node) + assert bl.tree[bl.lowest_common_ancestor(80, 55, 2)].key == 70 + assert bl.tree[bl.lowest_common_ancestor(60, 70, 2)].key == 70 + assert bl.lowest_common_ancestor(-3, 4) is None raises(ValueError, lambda: BST(root_data=6)) def test_BinaryTreeTraversal(): From b894bcebf9682deccd4975469f3428d15a57ab96 Mon Sep 17 00:00:00 2001 From: nethish Date: Sat, 1 Feb 2020 00:00:09 +0530 Subject: [PATCH 02/13] Added tests for LCA and code correction --- pydatastructs/trees/binary_trees.py | 6 +++-- .../trees/tests/test_binary_trees.py | 22 +++++++++++++++++-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index c195aaecd..95adb1b3b 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -427,7 +427,6 @@ def _simple_path(self, key, root, path): if self._simple_path(key, self.tree[root].left, path) or \ self._simple_path(key, self.tree[root].right, path): - path.append(root) return True path.pop() @@ -504,6 +503,7 @@ def lowest_common_ancestor(self, j, k, algorithm=1): root = self.root_idx path1 = self.simple_path(j) path2 = self.simple_path(k) + print(path1, path2) if not path1 or not path2: return None @@ -514,7 +514,9 @@ def lowest_common_ancestor(self, j, k, algorithm=1): return path1[i - 1] i += 1 j += 1 - return None + if path1 < path2: + return path1[-1] + return path2[-1] class AVLTree(BinarySearchTree): """ diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index f0bec4eb8..bec6ea25a 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -55,12 +55,30 @@ def test_BinarySearchTree(): assert b.delete(-3) is True assert b.delete(-13) is None bl = BST() - nodes = [50, 30, 90, 70, 100, 60, 80, 55] + nodes = [50, 30, 90, 70, 100, 60, 80, 55, 20, 40, 15, 10, 16, 17, 18] for node in nodes: bl.insert(node, node) assert bl.tree[bl.lowest_common_ancestor(80, 55, 2)].key == 70 assert bl.tree[bl.lowest_common_ancestor(60, 70, 2)].key == 70 - assert bl.lowest_common_ancestor(-3, 4) is None + assert bl.tree[bl.lowest_common_ancestor(18, 18, 2)].key == 18 + assert bl.tree[bl.lowest_common_ancestor(40, 90, 2)].key == 50 + assert bl.lowest_common_ancestor(60, 200, 2) is None + assert bl.lowest_common_ancestor(200, 60, 2) is None + assert bl.tree[bl.lowest_common_ancestor(18, 10, 2)].key == 15 + assert bl.tree[bl.lowest_common_ancestor(55, 100, 2)].key == 90 + assert bl.tree[bl.lowest_common_ancestor(16, 80, 2)].key == 50 + assert bl.lowest_common_ancestor(-3, 4, 2) is None + + assert bl.tree[bl.lowest_common_ancestor(80, 55, 1)].key == 70 + assert bl.tree[bl.lowest_common_ancestor(60, 70, 1)].key == 70 + assert bl.lowest_common_ancestor(-3, 4, 1) is None + assert bl.tree[bl.lowest_common_ancestor(18, 18, 1)].key == 18 + assert bl.tree[bl.lowest_common_ancestor(40, 90, 1)].key == 50 + assert bl.lowest_common_ancestor(60, 200, 1) is None + assert bl.lowest_common_ancestor(200, 60, 1) is None + assert bl.tree[bl.lowest_common_ancestor(18, 10, 1)].key == 15 + assert bl.tree[bl.lowest_common_ancestor(55, 100, 1)].key == 90 + assert bl.tree[bl.lowest_common_ancestor(16, 80, 1)].key == 50 raises(ValueError, lambda: BST(root_data=6)) def test_BinaryTreeTraversal(): From 8dd5d239d073b5db6d27ebcda6a785b5821f0fbf Mon Sep 17 00:00:00 2001 From: nethish Date: Sat, 1 Feb 2020 00:06:11 +0530 Subject: [PATCH 03/13] Covered corner testcases --- pydatastructs/trees/tests/test_binary_trees.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index bec6ea25a..64601672d 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -68,6 +68,7 @@ def test_BinarySearchTree(): assert bl.tree[bl.lowest_common_ancestor(55, 100, 2)].key == 90 assert bl.tree[bl.lowest_common_ancestor(16, 80, 2)].key == 50 assert bl.lowest_common_ancestor(-3, 4, 2) is None + assert bl.tree[bl.lowest_common_ancestor(30, 55, 2)].key == 50 assert bl.tree[bl.lowest_common_ancestor(80, 55, 1)].key == 70 assert bl.tree[bl.lowest_common_ancestor(60, 70, 1)].key == 70 @@ -79,6 +80,7 @@ def test_BinarySearchTree(): assert bl.tree[bl.lowest_common_ancestor(18, 10, 1)].key == 15 assert bl.tree[bl.lowest_common_ancestor(55, 100, 1)].key == 90 assert bl.tree[bl.lowest_common_ancestor(16, 80, 1)].key == 50 + assert bl.tree[bl.lowest_common_ancestor(30, 55, 1)].key == 50 raises(ValueError, lambda: BST(root_data=6)) def test_BinaryTreeTraversal(): From 814daadb6222db2be936cb44ee0e850786101ef1 Mon Sep 17 00:00:00 2001 From: nethish Date: Sat, 1 Feb 2020 00:39:11 +0530 Subject: [PATCH 04/13] Added docs for simple path --- pydatastructs/trees/binary_trees.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 95adb1b3b..58df56f2b 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -433,6 +433,23 @@ def _simple_path(self, key, root, path): return False def simple_path(self, key): + """ + Return the intermediate nodes between root and node with given key + + Parameters + ========== + + key: Node.key + key of the node for which path to be found + + Returns + ======= + + list + List of intermediate nodes. + Empty list if node is not present. + + """ path = [] self._simple_path(key, self.root_idx, path) return path From f9099ba248a8af2409275f84090d3ea8b76e44e6 Mon Sep 17 00:00:00 2001 From: nethish Date: Sat, 1 Feb 2020 00:42:06 +0530 Subject: [PATCH 05/13] Added docs for simple path --- pydatastructs/trees/binary_trees.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 58df56f2b..f7f562a8c 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -433,23 +433,23 @@ def _simple_path(self, key, root, path): return False def simple_path(self, key): - """ - Return the intermediate nodes between root and node with given key + """ + Return the intermediate nodes between root and node with given key - Parameters - ========== + Parameters + ========== - key: Node.key - key of the node for which path to be found + key: Node.key + key of the node for which path to be found - Returns - ======= + Returns + ======= - list - List of intermediate nodes. - Empty list if node is not present. + list + List of intermediate nodes. + Empty list if node is not present. - """ + """ path = [] self._simple_path(key, self.root_idx, path) return path From 994e2671e20483828089f18d29083a03f8ea1ebd Mon Sep 17 00:00:00 2001 From: nethish Date: Sun, 2 Feb 2020 07:54:58 +0530 Subject: [PATCH 06/13] Merged simple_path functions --- pydatastructs/trees/binary_trees.py | 31 +++++------------------------ 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index f7f562a8c..5e8630125 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -415,7 +415,7 @@ def rank(self, x): walk = p return r - def _simple_path(self, key, root, path): + def _simple_path(self, key, root, path=[]): """ Utility funtion to find the simple path between root and node. """ @@ -432,28 +432,6 @@ def _simple_path(self, key, root, path): path.pop() return False - def simple_path(self, key): - """ - Return the intermediate nodes between root and node with given key - - Parameters - ========== - - key: Node.key - key of the node for which path to be found - - Returns - ======= - - list - List of intermediate nodes. - Empty list if node is not present. - - """ - path = [] - self._simple_path(key, self.root_idx, path) - return path - def lowest_common_ancestor(self, j, k, algorithm=1): """ @@ -517,10 +495,11 @@ def lowest_common_ancestor(self, j, k, algorithm=1): v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) return curr_root else: + path1, path2 = [], [] root = self.root_idx - path1 = self.simple_path(j) - path2 = self.simple_path(k) - print(path1, path2) + self._simple_path(j, root, path1) + self._simple_path(k, root, path2) + if not path1 or not path2: return None From 94817e00d974ceb95a94f94e74cea9003d80aa38 Mon Sep 17 00:00:00 2001 From: nethish Date: Sun, 2 Feb 2020 08:07:10 +0530 Subject: [PATCH 07/13] Merged simple_path functions and added docs --- pydatastructs/trees/binary_trees.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 5e8630125..adc3ef62b 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -418,6 +418,21 @@ def rank(self, x): def _simple_path(self, key, root, path=[]): """ Utility funtion to find the simple path between root and node. + + Parameter + ========= + + key: + node to be searched + + path: + Stores path from root to the node to be found + + Returns + ======= + + bool: + True of path found else False """ if root is None: return False From a53d277f0364ff3a2327ee8299bb30d798f127d0 Mon Sep 17 00:00:00 2001 From: nethish Date: Sun, 2 Feb 2020 08:31:36 +0530 Subject: [PATCH 08/13] Modified LCA to return key --- pydatastructs/trees/binary_trees.py | 26 ++++++++---- .../trees/tests/test_binary_trees.py | 40 +++++++++++-------- 2 files changed, 41 insertions(+), 25 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index adc3ef62b..580757db7 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -432,7 +432,7 @@ def _simple_path(self, key, root, path=[]): ======= bool: - True of path found else False + True if path found else False """ if root is None: return False @@ -499,35 +499,45 @@ def lowest_common_ancestor(self, j, k, algorithm=1): return None u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) + while not (u_left ^ v_left): if u_left and v_left: curr_root = self.tree[curr_root].left else: curr_root = self.tree[curr_root].right + if curr_root == u or curr_root == v: - return curr_root + if curr_root is None: + break + return self.tree[curr_root].key + u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) - return curr_root + + print(j, k, self.tree[curr_root].key) + if curr_root is None: + return curr_root + return self.tree[curr_root].key + else: path1, path2 = [], [] root = self.root_idx self._simple_path(j, root, path1) self._simple_path(k, root, path2) - + key = None if not path1 or not path2: - return None + return key n, m = len(path1), len(path2) i = j = 0 while i < n and j < m: if path1[i] != path2[j]: - return path1[i - 1] + return self.tree[path1[i - 1]].key i += 1 j += 1 if path1 < path2: - return path1[-1] - return path2[-1] + return self.tree[path1[-1]].key + return self.tree[path2[-1]].key class AVLTree(BinarySearchTree): """ diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 64601672d..8963985e2 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -58,29 +58,35 @@ def test_BinarySearchTree(): nodes = [50, 30, 90, 70, 100, 60, 80, 55, 20, 40, 15, 10, 16, 17, 18] for node in nodes: bl.insert(node, node) - assert bl.tree[bl.lowest_common_ancestor(80, 55, 2)].key == 70 - assert bl.tree[bl.lowest_common_ancestor(60, 70, 2)].key == 70 - assert bl.tree[bl.lowest_common_ancestor(18, 18, 2)].key == 18 - assert bl.tree[bl.lowest_common_ancestor(40, 90, 2)].key == 50 + + assert bl.lowest_common_ancestor(80, 55, 2) == 70 + assert bl.lowest_common_ancestor(60, 70, 2) == 70 + assert bl.lowest_common_ancestor(18, 18, 2) == 18 + assert bl.lowest_common_ancestor(40, 90, 2) == 50 + + assert bl.lowest_common_ancestor(18, 10, 2) == 15 + assert bl.lowest_common_ancestor(55, 100, 2) == 90 + assert bl.lowest_common_ancestor(16, 80, 2) == 50 + assert bl.lowest_common_ancestor(30, 55, 2) == 50 + assert bl.lowest_common_ancestor(60, 200, 2) is None assert bl.lowest_common_ancestor(200, 60, 2) is None - assert bl.tree[bl.lowest_common_ancestor(18, 10, 2)].key == 15 - assert bl.tree[bl.lowest_common_ancestor(55, 100, 2)].key == 90 - assert bl.tree[bl.lowest_common_ancestor(16, 80, 2)].key == 50 assert bl.lowest_common_ancestor(-3, 4, 2) is None - assert bl.tree[bl.lowest_common_ancestor(30, 55, 2)].key == 50 - assert bl.tree[bl.lowest_common_ancestor(80, 55, 1)].key == 70 - assert bl.tree[bl.lowest_common_ancestor(60, 70, 1)].key == 70 - assert bl.lowest_common_ancestor(-3, 4, 1) is None - assert bl.tree[bl.lowest_common_ancestor(18, 18, 1)].key == 18 - assert bl.tree[bl.lowest_common_ancestor(40, 90, 1)].key == 50 + assert bl.lowest_common_ancestor(80, 55, 1) == 70 + assert bl.lowest_common_ancestor(60, 70, 1) == 70 + assert bl.lowest_common_ancestor(18, 18, 1) == 18 + assert bl.lowest_common_ancestor(40, 90, 1) == 50 + + assert bl.lowest_common_ancestor(18, 10, 1) == 15 + assert bl.lowest_common_ancestor(55, 100, 1) == 90 + assert bl.lowest_common_ancestor(16, 80, 1) == 50 + assert bl.lowest_common_ancestor(30, 55, 1) == 50 + assert bl.lowest_common_ancestor(60, 200, 1) is None assert bl.lowest_common_ancestor(200, 60, 1) is None - assert bl.tree[bl.lowest_common_ancestor(18, 10, 1)].key == 15 - assert bl.tree[bl.lowest_common_ancestor(55, 100, 1)].key == 90 - assert bl.tree[bl.lowest_common_ancestor(16, 80, 1)].key == 50 - assert bl.tree[bl.lowest_common_ancestor(30, 55, 1)].key == 50 + assert bl.lowest_common_ancestor(-3, 4, 2) is None + raises(ValueError, lambda: BST(root_data=6)) def test_BinaryTreeTraversal(): From 00ad7f70152733fa1d38755d905780ec7462c23f Mon Sep 17 00:00:00 2001 From: nethish Date: Sun, 2 Feb 2020 11:36:51 +0530 Subject: [PATCH 09/13] Removed extra statements --- pydatastructs/trees/binary_trees.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 580757db7..58310b69b 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -508,13 +508,12 @@ def lowest_common_ancestor(self, j, k, algorithm=1): if curr_root == u or curr_root == v: if curr_root is None: - break + return None return self.tree[curr_root].key u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) - print(j, k, self.tree[curr_root].key) if curr_root is None: return curr_root return self.tree[curr_root].key From f997c530b9d0ffb634e1d41b24921e8d0774d5d5 Mon Sep 17 00:00:00 2001 From: nethish Date: Thu, 27 Feb 2020 16:03:41 +0530 Subject: [PATCH 10/13] Implemented simple path using stack --- pydatastructs/trees/binary_trees.py | 60 +++++++++++++++++------------ 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 58310b69b..58c984801 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -415,7 +415,7 @@ def rank(self, x): walk = p return r - def _simple_path(self, key, root, path=[]): + def _simple_path(self, key, root): """ Utility funtion to find the simple path between root and node. @@ -423,10 +423,7 @@ def _simple_path(self, key, root, path=[]): ========= key: - node to be searched - - path: - Stores path from root to the node to be found + key of the node to be searched Returns ======= @@ -434,18 +431,30 @@ def _simple_path(self, key, root, path=[]): bool: True if path found else False """ - if root is None: - return False - path.append(root) - if self.tree[root].key == key: - return True - - if self._simple_path(key, self.tree[root].left, path) or \ - self._simple_path(key, self.tree[root].right, path): - return True - - path.pop() - return False + stack = Stack() + stack.push(root) + path = [] + node_idx = -1 + + while not stack.is_empty: + node = stack.pop() + if self.tree[node].key == key: + node_idx = node + break + if self.tree[node].left: + stack.push(self.tree[node].left) + if self.tree[node].right: + stack.push(self.tree[node].right) + + if node_idx == -1: + return path + + while node_idx != 0: + path.append(node_idx) + node_idx = self.tree[node_idx].parent + path.append(0) + + return path[::-1] def lowest_common_ancestor(self, j, k, algorithm=1): @@ -497,8 +506,10 @@ def lowest_common_ancestor(self, j, k, algorithm=1): u, v = self.search(j), self.search(k) if (u is None) or (v is None): return None - u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) - v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) + u_left = self.comparator(self.tree[u].key, \ + self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, \ + self.tree[curr_root].key) while not (u_left ^ v_left): if u_left and v_left: @@ -511,18 +522,19 @@ def lowest_common_ancestor(self, j, k, algorithm=1): return None return self.tree[curr_root].key - u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key) - v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key) + u_left = self.comparator(self.tree[u].key, \ + self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, \ + self.tree[curr_root].key) if curr_root is None: return curr_root return self.tree[curr_root].key else: - path1, path2 = [], [] root = self.root_idx - self._simple_path(j, root, path1) - self._simple_path(k, root, path2) + path1 = self._simple_path(j, root) + path2 = self._simple_path(k, root) key = None if not path1 or not path2: return key From 3fa37609569a6acc94321860d5bf6ee6b7a1b4ae Mon Sep 17 00:00:00 2001 From: nethish Date: Thu, 27 Feb 2020 16:06:37 +0530 Subject: [PATCH 11/13] Implemented simple path using stack --- pydatastructs/trees/binary_trees.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 58c984801..7c7646888 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -445,15 +445,15 @@ def _simple_path(self, key, root): stack.push(self.tree[node].left) if self.tree[node].right: stack.push(self.tree[node].right) - + if node_idx == -1: return path - + while node_idx != 0: path.append(node_idx) node_idx = self.tree[node_idx].parent path.append(0) - + return path[::-1] def lowest_common_ancestor(self, j, k, algorithm=1): From e37ee7dac4d74770e6b70d051be58dd725411e6a Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Thu, 5 Mar 2020 15:51:26 +0530 Subject: [PATCH 12/13] final patch --- pydatastructs/trees/binary_trees.py | 119 ++++++++++++++-------------- 1 file changed, 60 insertions(+), 59 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index 7c7646888..a73adf8f4 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -422,15 +422,15 @@ def _simple_path(self, key, root): Parameter ========= - key: - key of the node to be searched + key: Node.key + Key of the node to be searched Returns ======= - bool: - True if path found else False + path: list """ + stack = Stack() stack.push(root) path = [] @@ -453,8 +453,58 @@ def _simple_path(self, key, root): path.append(node_idx) node_idx = self.tree[node_idx].parent path.append(0) + path.reverse() + + return path + + def _lca_1(self, j, k): + root = self.root_idx + path1 = self._simple_path(j, root) + path2 = self._simple_path(k, root) + key = None + if not path1 or not path2: + return key + + n, m = len(path1), len(path2) + i = j = 0 + while i < n and j < m: + if path1[i] != path2[j]: + return self.tree[path1[i - 1]].key + i += 1 + j += 1 + if path1 < path2: + return self.tree[path1[-1]].key + return self.tree[path2[-1]].key + + def _lca_2(self, j, k): + curr_root = self.root_idx + u, v = self.search(j), self.search(k) + if (u is None) or (v is None): + return None + u_left = self.comparator(self.tree[u].key, \ + self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, \ + self.tree[curr_root].key) + + while not (u_left ^ v_left): + if u_left and v_left: + curr_root = self.tree[curr_root].left + else: + curr_root = self.tree[curr_root].right - return path[::-1] + if curr_root == u or curr_root == v: + if curr_root is None: + return None + return self.tree[curr_root].key + + u_left = self.comparator(self.tree[u].key, \ + self.tree[curr_root].key) + v_left = self.comparator(self.tree[v].key, \ + self.tree[curr_root].key) + + if curr_root is None: + return curr_root + return self.tree[curr_root].key def lowest_common_ancestor(self, j, k, algorithm=1): @@ -465,9 +515,9 @@ def lowest_common_ancestor(self, j, k, algorithm=1): ========== j: Node.key - key of first node + Key of first node k: Node.key - key of second node + Key of second node algorithm: int The algorithm to be used for computing the lowest common ancestor. @@ -487,11 +537,9 @@ def lowest_common_ancestor(self, j, k, algorithm=1): Returns ======= - int - The index of the lowest common ancestor in the tree. + Node.key + The key of the lowest common ancestor in the tree. if both the nodes are present in the tree. - None - In all other cases. References ========== @@ -501,54 +549,7 @@ def lowest_common_ancestor(self, j, k, algorithm=1): .. [2] https://pdfs.semanticscholar.org/e75b/386cc554214aa0ebd6bd6dbdd0e490da3739.pdf """ - if algorithm == 1: - curr_root = self.root_idx - u, v = self.search(j), self.search(k) - if (u is None) or (v is None): - return None - u_left = self.comparator(self.tree[u].key, \ - self.tree[curr_root].key) - v_left = self.comparator(self.tree[v].key, \ - self.tree[curr_root].key) - - while not (u_left ^ v_left): - if u_left and v_left: - curr_root = self.tree[curr_root].left - else: - curr_root = self.tree[curr_root].right - - if curr_root == u or curr_root == v: - if curr_root is None: - return None - return self.tree[curr_root].key - - u_left = self.comparator(self.tree[u].key, \ - self.tree[curr_root].key) - v_left = self.comparator(self.tree[v].key, \ - self.tree[curr_root].key) - - if curr_root is None: - return curr_root - return self.tree[curr_root].key - - else: - root = self.root_idx - path1 = self._simple_path(j, root) - path2 = self._simple_path(k, root) - key = None - if not path1 or not path2: - return key - - n, m = len(path1), len(path2) - i = j = 0 - while i < n and j < m: - if path1[i] != path2[j]: - return self.tree[path1[i - 1]].key - i += 1 - j += 1 - if path1 < path2: - return self.tree[path1[-1]].key - return self.tree[path2[-1]].key + getattr(self, "_lca_"+str(algorithm))(self, j, k) class AVLTree(BinarySearchTree): """ From 94d879c8b88b607127a2be0589a4eaf97f68c0a3 Mon Sep 17 00:00:00 2001 From: czgdp1807 Date: Thu, 5 Mar 2020 16:03:34 +0530 Subject: [PATCH 13/13] final tests --- pydatastructs/trees/binary_trees.py | 17 +++++++++-------- pydatastructs/trees/tests/test_binary_trees.py | 14 ++++++-------- 2 files changed, 15 insertions(+), 16 deletions(-) diff --git a/pydatastructs/trees/binary_trees.py b/pydatastructs/trees/binary_trees.py index a73adf8f4..fce55fb21 100644 --- a/pydatastructs/trees/binary_trees.py +++ b/pydatastructs/trees/binary_trees.py @@ -430,7 +430,7 @@ def _simple_path(self, key, root): path: list """ - + stack = Stack() stack.push(root) path = [] @@ -454,16 +454,16 @@ def _simple_path(self, key, root): node_idx = self.tree[node_idx].parent path.append(0) path.reverse() - + return path - + def _lca_1(self, j, k): root = self.root_idx path1 = self._simple_path(j, root) path2 = self._simple_path(k, root) - key = None if not path1 or not path2: - return key + raise ValueError("One of two path doesn't exists. See %s, %s" + %(path1, path2)) n, m = len(path1), len(path2) i = j = 0 @@ -475,12 +475,13 @@ def _lca_1(self, j, k): if path1 < path2: return self.tree[path1[-1]].key return self.tree[path2[-1]].key - + def _lca_2(self, j, k): curr_root = self.root_idx u, v = self.search(j), self.search(k) if (u is None) or (v is None): - return None + raise ValueError("One of the nodes with key %s " + "or %s doesn't exits"%(j, k)) u_left = self.comparator(self.tree[u].key, \ self.tree[curr_root].key) v_left = self.comparator(self.tree[v].key, \ @@ -549,7 +550,7 @@ def lowest_common_ancestor(self, j, k, algorithm=1): .. [2] https://pdfs.semanticscholar.org/e75b/386cc554214aa0ebd6bd6dbdd0e490da3739.pdf """ - getattr(self, "_lca_"+str(algorithm))(self, j, k) + return getattr(self, "_lca_"+str(algorithm))(j, k) class AVLTree(BinarySearchTree): """ diff --git a/pydatastructs/trees/tests/test_binary_trees.py b/pydatastructs/trees/tests/test_binary_trees.py index 8963985e2..1bf391913 100644 --- a/pydatastructs/trees/tests/test_binary_trees.py +++ b/pydatastructs/trees/tests/test_binary_trees.py @@ -69,9 +69,9 @@ def test_BinarySearchTree(): assert bl.lowest_common_ancestor(16, 80, 2) == 50 assert bl.lowest_common_ancestor(30, 55, 2) == 50 - assert bl.lowest_common_ancestor(60, 200, 2) is None - assert bl.lowest_common_ancestor(200, 60, 2) is None - assert bl.lowest_common_ancestor(-3, 4, 2) is None + assert raises(ValueError, lambda: bl.lowest_common_ancestor(60, 200, 2)) + assert raises(ValueError, lambda: bl.lowest_common_ancestor(200, 60, 2)) + assert raises(ValueError, lambda: bl.lowest_common_ancestor(-3, 4, 2)) assert bl.lowest_common_ancestor(80, 55, 1) == 70 assert bl.lowest_common_ancestor(60, 70, 1) == 70 @@ -83,11 +83,9 @@ def test_BinarySearchTree(): assert bl.lowest_common_ancestor(16, 80, 1) == 50 assert bl.lowest_common_ancestor(30, 55, 1) == 50 - assert bl.lowest_common_ancestor(60, 200, 1) is None - assert bl.lowest_common_ancestor(200, 60, 1) is None - assert bl.lowest_common_ancestor(-3, 4, 2) is None - - raises(ValueError, lambda: BST(root_data=6)) + assert raises(ValueError, lambda: bl.lowest_common_ancestor(60, 200, 1)) + assert raises(ValueError, lambda: bl.lowest_common_ancestor(200, 60, 1)) + assert raises(ValueError, lambda: bl.lowest_common_ancestor(-3, 4, 1)) def test_BinaryTreeTraversal(): BST = BinarySearchTree