Skip to content

Added LCA and tests #88

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

Merged
merged 13 commits into from
Mar 5, 2020
120 changes: 120 additions & 0 deletions pydatastructs/trees/binary_trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,126 @@ 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):
return True
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please convert this into iterative logic using pydatastructs.Stack as Python has a finite recursion limit irrespective of the size of heap memory.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stack hasn't been implemented yet.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes. I made all the changes you have mentioned except this.
pydatastruct.stack is still abstract.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just now saw that.
Ill start making the changes

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.


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):

"""
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
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A value error would have been better to let the user know that they are searching for garbage values.

u_left = self.comparator(self.tree[u].key, self.tree[curr_root].key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please split this into two lines. Doesn't look clean.

v_left = self.comparator(self.tree[v].key, self.tree[curr_root].key)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

+1

return curr_root
else:
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

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
if path1 < path2:
return path1[-1]
return path2[-1]

class AVLTree(BinarySearchTree):
"""
Represents AVL trees.
Expand Down
27 changes: 27 additions & 0 deletions pydatastructs/trees/tests/test_binary_trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,33 @@ 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, 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(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(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
raises(ValueError, lambda: BST(root_data=6))

def test_BinaryTreeTraversal():
Expand Down