Skip to content
123 changes: 123 additions & 0 deletions pydatastructs/trees/binary_trees.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,129 @@ 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.

Parameter
=========

key:
Copy link
Member

Choose a reason for hiding this comment

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

key: Python object

Copy link
Member

Choose a reason for hiding this comment

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

This isn't addressed.

node to be searched

path:
Copy link
Member

Choose a reason for hiding this comment

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

path: list
Though I don't think it is needed in the API.

Stores path from root to the node to be found

Returns
=======

bool:
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
bool:
path[::-1]: 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
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 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:
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)
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


if curr_root is None:
return curr_root
return 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.

key may not always be an integer. So, this conflicts with the doc string.


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

class AVLTree(BinarySearchTree):
"""
Represents AVL trees.
Expand Down
33 changes: 33 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,39 @@ 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.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.lowest_common_ancestor(-3, 4, 2) is None

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.lowest_common_ancestor(-3, 4, 2) is None

raises(ValueError, lambda: BST(root_data=6))

def test_BinaryTreeTraversal():
Expand Down