Skip to content
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