Python/data_structures/binary_tree/binary_search_tree.py

262 lines
7.7 KiB
Python
Raw Normal View History

2019-10-05 05:14:13 +00:00
"""
2018-10-19 12:48:28 +00:00
A binary search Tree
2019-10-05 05:14:13 +00:00
"""
2018-10-19 12:48:28 +00:00
2019-10-05 05:14:13 +00:00
class Node:
2018-10-19 12:48:28 +00:00
def __init__(self, label, parent):
self.label = label
self.left = None
self.right = None
2019-10-05 05:14:13 +00:00
# Added in order to delete a node easier
2018-10-19 12:48:28 +00:00
self.parent = parent
def getLabel(self):
return self.label
def setLabel(self, label):
self.label = label
def getLeft(self):
return self.left
def setLeft(self, left):
self.left = left
def getRight(self):
return self.right
def setRight(self, right):
self.right = right
def getParent(self):
return self.parent
def setParent(self, parent):
self.parent = parent
2019-10-05 05:14:13 +00:00
class BinarySearchTree:
2018-10-19 12:48:28 +00:00
def __init__(self):
self.root = None
def insert(self, label):
# Create a new Node
new_node = Node(label, None)
# If Tree is empty
if self.empty():
self.root = new_node
else:
2019-10-05 05:14:13 +00:00
# If Tree is not empty
2018-10-19 12:48:28 +00:00
curr_node = self.root
2019-10-05 05:14:13 +00:00
# While we don't get to a leaf
2018-10-19 12:48:28 +00:00
while curr_node is not None:
2019-10-05 05:14:13 +00:00
# We keep reference of the parent node
2018-10-19 12:48:28 +00:00
parent_node = curr_node
2019-10-05 05:14:13 +00:00
# If node label is less than current node
2018-10-19 12:48:28 +00:00
if new_node.getLabel() < curr_node.getLabel():
2019-10-05 05:14:13 +00:00
# We go left
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getLeft()
else:
2019-10-05 05:14:13 +00:00
# Else we go right
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getRight()
2019-10-05 05:14:13 +00:00
# We insert the new node in a leaf
2018-10-19 12:48:28 +00:00
if new_node.getLabel() < parent_node.getLabel():
parent_node.setLeft(new_node)
else:
parent_node.setRight(new_node)
2019-10-05 05:14:13 +00:00
# Set parent to the new node
new_node.setParent(parent_node)
2018-10-19 12:48:28 +00:00
def delete(self, label):
2019-10-05 05:14:13 +00:00
if not self.empty():
# Look for the node with that label
2018-10-19 12:48:28 +00:00
node = self.getNode(label)
2019-10-05 05:14:13 +00:00
# If the node exists
if node is not None:
# If it has no children
if node.getLeft() is None and node.getRight() is None:
2018-10-19 12:48:28 +00:00
self.__reassignNodes(node, None)
node = None
2019-10-05 05:14:13 +00:00
# Has only right children
elif node.getLeft() is None and node.getRight() is not None:
2018-10-19 12:48:28 +00:00
self.__reassignNodes(node, node.getRight())
2019-10-05 05:14:13 +00:00
# Has only left children
elif node.getLeft() is not None and node.getRight() is None:
2018-10-19 12:48:28 +00:00
self.__reassignNodes(node, node.getLeft())
2019-10-05 05:14:13 +00:00
# Has two children
2018-10-19 12:48:28 +00:00
else:
2019-10-05 05:14:13 +00:00
# Gets the max value of the left branch
2018-10-19 12:48:28 +00:00
tmpNode = self.getMax(node.getLeft())
2019-10-05 05:14:13 +00:00
# Deletes the tmpNode
2018-10-19 12:48:28 +00:00
self.delete(tmpNode.getLabel())
2019-10-05 05:14:13 +00:00
# Assigns the value to the node to delete and keesp tree structure
2018-10-19 12:48:28 +00:00
node.setLabel(tmpNode.getLabel())
2018-10-19 12:48:28 +00:00
def getNode(self, label):
curr_node = None
2019-10-05 05:14:13 +00:00
# If the tree is not empty
if not self.empty():
# Get tree root
2018-10-19 12:48:28 +00:00
curr_node = self.getRoot()
2019-10-05 05:14:13 +00:00
# While we don't find the node we look for
# I am using lazy evaluation here to avoid NoneType Attribute error
2018-10-19 12:48:28 +00:00
while curr_node is not None and curr_node.getLabel() is not label:
2019-10-05 05:14:13 +00:00
# If node label is less than current node
2018-10-19 12:48:28 +00:00
if label < curr_node.getLabel():
2019-10-05 05:14:13 +00:00
# We go left
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getLeft()
else:
2019-10-05 05:14:13 +00:00
# Else we go right
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getRight()
return curr_node
2019-10-05 05:14:13 +00:00
def getMax(self, root=None):
if root is not None:
2018-10-19 12:48:28 +00:00
curr_node = root
else:
2019-10-05 05:14:13 +00:00
# We go deep on the right branch
2018-10-19 12:48:28 +00:00
curr_node = self.getRoot()
2019-10-05 05:14:13 +00:00
if not self.empty():
while curr_node.getRight() is not None:
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getRight()
return curr_node
2019-10-05 05:14:13 +00:00
def getMin(self, root=None):
if root is not None:
2018-10-19 12:48:28 +00:00
curr_node = root
else:
2019-10-05 05:14:13 +00:00
# We go deep on the left branch
2018-10-19 12:48:28 +00:00
curr_node = self.getRoot()
2019-10-05 05:14:13 +00:00
if not self.empty():
2018-10-19 12:48:28 +00:00
curr_node = self.getRoot()
2019-10-05 05:14:13 +00:00
while curr_node.getLeft() is not None:
2018-10-19 12:48:28 +00:00
curr_node = curr_node.getLeft()
return curr_node
def empty(self):
if self.root is None:
return True
return False
def __InOrderTraversal(self, curr_node):
nodeList = []
if curr_node is not None:
nodeList.insert(0, curr_node)
nodeList = nodeList + self.__InOrderTraversal(curr_node.getLeft())
nodeList = nodeList + self.__InOrderTraversal(curr_node.getRight())
return nodeList
def getRoot(self):
return self.root
def __isRightChildren(self, node):
2019-10-05 05:14:13 +00:00
if node == node.getParent().getRight():
2018-10-19 12:48:28 +00:00
return True
return False
def __reassignNodes(self, node, newChildren):
2019-10-05 05:14:13 +00:00
if newChildren is not None:
2018-10-19 12:48:28 +00:00
newChildren.setParent(node.getParent())
2019-10-05 05:14:13 +00:00
if node.getParent() is not None:
# If it is the Right Children
if self.__isRightChildren(node):
2018-10-19 12:48:28 +00:00
node.getParent().setRight(newChildren)
else:
2019-10-05 05:14:13 +00:00
# Else it is the left children
2018-10-19 12:48:28 +00:00
node.getParent().setLeft(newChildren)
2019-10-05 05:14:13 +00:00
# This function traversal the tree. By default it returns an
# In order traversal list. You can pass a function to traversal
# The tree as needed by client code
def traversalTree(self, traversalFunction=None, root=None):
if traversalFunction is None:
# Returns a list of nodes in preOrder by default
2018-10-19 12:48:28 +00:00
return self.__InOrderTraversal(self.root)
else:
2019-10-05 05:14:13 +00:00
# Returns a list of nodes in the order that the users wants to
2018-10-19 12:48:28 +00:00
return traversalFunction(self.root)
2019-10-05 05:14:13 +00:00
# Returns an string of all the nodes labels in the list
# In Order Traversal
2018-10-19 12:48:28 +00:00
def __str__(self):
list = self.__InOrderTraversal(self.root)
str = ""
for x in list:
str = str + " " + x.getLabel().__str__()
return str
2019-10-05 05:14:13 +00:00
2018-10-19 12:48:28 +00:00
def InPreOrder(curr_node):
nodeList = []
if curr_node is not None:
nodeList = nodeList + InPreOrder(curr_node.getLeft())
nodeList.insert(0, curr_node.getLabel())
nodeList = nodeList + InPreOrder(curr_node.getRight())
return nodeList
2019-10-05 05:14:13 +00:00
2018-10-19 12:48:28 +00:00
def testBinarySearchTree():
2019-10-05 05:14:13 +00:00
r"""
2018-10-19 12:48:28 +00:00
Example
8
/ \
3 10
/ \ \
1 6 14
/ \ /
4 7 13
2019-10-05 05:14:13 +00:00
"""
2018-10-19 12:48:28 +00:00
2019-10-05 05:14:13 +00:00
r"""
2018-10-19 12:48:28 +00:00
Example After Deletion
7
/ \
1 4
2019-10-05 05:14:13 +00:00
"""
2018-10-19 12:48:28 +00:00
t = BinarySearchTree()
t.insert(8)
t.insert(3)
t.insert(6)
t.insert(1)
t.insert(10)
t.insert(14)
t.insert(13)
t.insert(4)
t.insert(7)
2019-10-05 05:14:13 +00:00
# Prints all the elements of the list in order traversal
2018-10-19 12:48:28 +00:00
print(t.__str__())
2019-10-05 05:14:13 +00:00
if t.getNode(6) is not None:
2018-10-19 12:48:28 +00:00
print("The label 6 exists")
else:
print("The label 6 doesn't exist")
2019-10-05 05:14:13 +00:00
if t.getNode(-1) is not None:
2018-10-19 12:48:28 +00:00
print("The label -1 exists")
else:
print("The label -1 doesn't exist")
2019-10-05 05:14:13 +00:00
if not t.empty():
2018-10-19 12:48:28 +00:00
print(("Max Value: ", t.getMax().getLabel()))
print(("Min Value: ", t.getMin().getLabel()))
2018-10-19 12:48:28 +00:00
t.delete(13)
t.delete(10)
t.delete(8)
t.delete(3)
t.delete(6)
t.delete(14)
2019-10-05 05:14:13 +00:00
# Gets all the elements of the tree In pre order
# And it prints them
2018-10-19 12:48:28 +00:00
list = t.traversalTree(InPreOrder, t.root)
for x in list:
print(x)
2019-10-05 05:14:13 +00:00
2018-10-19 12:48:28 +00:00
if __name__ == "__main__":
testBinarySearchTree()