enh(Python): binary tree (#665)

pull/540/head^2
Cem Bağlum 2022-01-13 15:55:01 +03:00 committed by GitHub
parent 98bac46911
commit fd9f7b4806
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 160 additions and 151 deletions

View File

@ -66,5 +66,5 @@
## Trees ## Trees
- [Binary Tree](trees/binary_tree.py) - [Binary Tree](trees/binary_tree.py)
- [Binary Search Tree](trees/binary-search-tree.py) - [Binary Search Tree](trees/binary_search_tree.py)

View File

@ -1,8 +1,5 @@
# author: @hashfx # author: @hashfx
# Binary Tree Data Structure
# Data is stored in hierarchical form where a parent node can have at most 2 child nodes
#
# A # A
# ___|___ # ___|___
# B C # B C
@ -10,49 +7,56 @@
# D E # D E
# / \ \ # / \ \
# F G H # F G H
#
# Here, A is "ROOT NODE" and B, C are "CHILD NODE" """ Binary Tree Data Structure
# (B-D-F-G), (B-E-H) is a sub-tree Data is stored in hierarchical form where a parent node can have at most 2 child nodes
# B is 'ROOT NODE' for D, E & D is 'ROOT NODE' for F, G & E is root node for H
# Those nodes [C, F, G, H] who do not have any child node are "LEAF NODE" Here, A is "ROOT NODE" and B, C are "CHILD NODE"
# (B-D-F-G), (B-E-H) is a sub-tree
# Rules for Binary Search Tree: B is 'ROOT NODE' for D, E & D is 'ROOT NODE' for F, G & E is root node for H
# > All nodes are unique Those nodes [C, F, G, H] who do not have any child node are "LEAF NODE"
# > Right sub-tree > Left sub-tree ===== Left sub-tree < Right sub-tree
# [Value(B<A AND C>A), Value(D<B AND E>B), Value(F<D AND G>D), Value(H>E)] Rules for Binary Search Tree:
# > One parent node can not have more than 2 child nodes > All nodes are unique
# > Elements are not duplicated > Right sub-tree > Left sub-tree ===== Left sub-tree < Right sub-tree
# Searching in Binary Tree: [Value(B<A AND C>A), Value(D<B AND E>B), Value(F<D AND G>D), Value(H>E)]
# Suppose we want to search E in the Tree: > One parent node can not have more than 2 child nodes
# > At Root Node(A) :: IF A>E THEN element would be at Left sub-Tree > Elements are not duplicated
# > At Left sub-Tree(B) :: IF B<E THEN element would be at Right of the sub-tree Searching in Binary Tree:
# Suppose we want to search E in the Tree:
# Significance of BST: > At Root Node(A) :: IF A>E THEN element would be at Left sub-Tree
# With every iteration, search space is reduced by 1/2 (half) > At Left sub-Tree(B) :: IF B<E THEN element would be at Right of the sub-tree
# Let no of nodes in a tree (n) be 8 then:
# n = 8 [8->4->2->1] {Search completed in 3 iterations} Significance of BST:
# 3 compared to 8 is log(2)8 = 2 With every iteration, search space is reduced by 1/2 (half)
# Search Complexity : O(log n) Let no of nodes in a tree (n) be 8 then:
# Insertion Complexity : O(log n) n = 8 [8->4->2->1] {Search completed in 3 iterations}
# 3 compared to 8 is log(2)8 = 2
# Search Complexity : O(log n)
# Types of BST: Insertion Complexity : O(log n)
# Breadth First Search
#
# Types of BST:
# Depth First Search Breadth First Search
# order here means base node
# > In Order Traversal : first visit left sub-tree >> root node >> right sub-tree [F-D-G-B-H-E-A-C]
# {Root node in between left and right tree} Depth First Search
# > Pre Order Traversal : root node >> left sub-tree >> right sub-tree [A-B-D-F-G-E-H-C] order here means base node
# {Root node before left and right tree} > In Order Traversal :
# > Post Order Traversal : left sub-tree >> right sub-tree >> root node [F-G-D-H-E-B-C-A] first visit left sub-tree >> root node >> right sub-tree [F-D-G-B-H-E-A-C]
# {Root node after left and right tree} {Root node in between left and right tree}
> Pre Order Traversal :
root node >> left sub-tree >> right sub-tree [A-B-D-F-G-E-H-C]
{Root node before left and right tree}
> Post Order Traversal :
left sub-tree >> right sub-tree >> root node [F-G-D-H-E-B-C-A]
{Root node after left and right tree}"""
class Node: class Node:
# constructor """ constructor """
def __init__(self, data): def __init__(self, data):
""" constructor """
self.data = data self.data = data
self.left = None self.left = None
self.right = None self.right = None
@ -63,11 +67,12 @@ class Node:
# checking if entered data is already present # checking if entered data is already present
if data == self.data: if data == self.data:
return return None
# if tree is empty means no node(root) at tree else incoming data will be treated as node(root(tree)) # if tree is empty means no node(root) at tree
# else incoming data will be treated as node(root(tree))
if self.data: if self.data:
''' check if data(right) > data(left) & node(parent)''' # check if data(right) > data(left) & node(parent)
if data < self.data: # data is smaller than data of node(parent) if data < self.data: # data is smaller than data of node(parent)
if self.left is None: # and if no element is present at left of node if self.left is None: # and if no element is present at left of node
self.left = Node(data) # insert data at left self.left = Node(data) # insert data at left
@ -82,23 +87,27 @@ class Node:
else: else:
self.data = data # if tree is empty; treat incoming data as root of the tree self.data = data # if tree is empty; treat incoming data as root of the tree
return None
def InOrderTraversal(self):
def in_order_traversal(self):
""" constructor """
elements = [] # list to be filled with all elements of BST in specific order elements = [] # list to be filled with all elements of BST in specific order
# In-order-Traversal : left sub-tree >> root node >> right sub-tree # In-order-Traversal : left sub-tree >> root node >> right sub-tree
if self.left: # put elements of left sub-tree in list[elements] if self.left: # put elements of left sub-tree in list[elements]
elements += self.left.InOrderTraversal() elements += self.left.in_order_traversal()
elements.append(self.data) # put root node data in list[elements] elements.append(self.data) # put root node data in list[elements]
if self.right: # put elements of right sub-tree in list[elements] if self.right: # put elements of right sub-tree in list[elements]
elements += self.right.InOrderTraversal() elements += self.right.in_order_traversal()
return elements # return list[elements] return elements # return list[elements]
def PreOrderTraversal(self): def pre_order_traversal(self):
""" constructor """
elements = [] elements = []
# Pre-Order-Traversal : root node >> left sub-tree >> right sub-tree # Pre-Order-Traversal : root node >> left sub-tree >> right sub-tree
@ -106,23 +115,24 @@ class Node:
elements.append(self.data) # put root node data in list[elements] elements.append(self.data) # put root node data in list[elements]
if self.left: # put elements of left sub-tree in list[elements] if self.left: # put elements of left sub-tree in list[elements]
elements += self.left.InOrderTraversal() elements += self.left.in_order_traversal()
if self.right: # put elements of right sub-tree in list[elements] if self.right: # put elements of right sub-tree in list[elements]
elements += self.right.InOrderTraversal() elements += self.right.in_order_traversal()
return elements # return list[elements] return elements # return list[elements]
def PostOrderTraversal(self): def post_order_traversal(self):
""" constructor """
elements = [] elements = []
# Pre-Order-Traversal : left sub-tree >> right sub-tree >> root node # Pre-Order-Traversal : left sub-tree >> right sub-tree >> root node
if self.left: # put elements of left sub-tree in list[elements] if self.left: # put elements of left sub-tree in list[elements]
elements += self.left.InOrderTraversal() elements += self.left.in_order_traversal()
if self.right: # put elements of right sub-tree in list[elements] if self.right: # put elements of right sub-tree in list[elements]
elements += self.right.InOrderTraversal() elements += self.right.in_order_traversal()
elements.append(self.data) # put root node data in list[elements] elements.append(self.data) # put root node data in list[elements]
@ -137,23 +147,22 @@ class Node:
# search for val in left sub-tree # search for val in left sub-tree
if self.left: if self.left:
return self.left.search(val) return self.left.search(val)
else:
return False return False
if val > self.data: if val > self.data:
# search for val in right sub-tree # search for val in right sub-tree
if self.right: if self.right:
return self.right.search(val) return self.right.search(val)
else:
return False return False
return None
def max(self): def max(self):
'''Maximum element of tree: keep searching on right sub-tree to find maximum element ''' '''Maximum element of tree: keep searching on right sub-tree to find maximum element '''
if self.right is None: # leaf node if self.right is None: # leaf node
return self.data return self.data
return self.right.max() return self.right.max()
def min(self): def min(self):
''' Minimum element of tree: keep searching on left sub-tree to find minimum element ''' ''' Minimum element of tree: keep searching on left sub-tree to find minimum element '''
if self.left is None: # leaf node if self.left is None: # leaf node
@ -162,6 +171,7 @@ class Node:
def delete(self, val): def delete(self, val):
""" constructor """
if val < self.data: # search for element in left sub-tree if val < self.data: # search for element in left sub-tree
if self.left: # check if there is any left sub-tree if self.left: # check if there is any left sub-tree
self.left = self.left.delete(val) # delete recursion self.left = self.left.delete(val) # delete recursion
@ -193,13 +203,13 @@ class Node:
def build_tree(elements): def build_tree(elements):
""" constructor """
root = Node(elements[0]) root = Node(elements[0])
for i in range(1, len(elements)): for i in range(1, len(elements)):
root.add_child(elements[i]) root.add_child(elements[i])
return root return root
# If build_tree() is not used # If build_tree() is not used
# root = Node(4) # root = Node(4)
# root.add_child(6) # root.add_child(6)
@ -208,29 +218,25 @@ def build_tree(elements):
# root.add_child(3) # root.add_child(3)
# root.add_child(8) # root.add_child(8)
# root.add_child(5) # root.add_child(5)
''' smaller elements will be displayed at left/top of root node <--> greater elements will be displayed at
right/bottom of root node '''
# root.display() # root.display()
# main method # main method
if __name__ == '__main__': if __name__ == '__main__':
# Numeric BST # Numeric BST
num_list = [20, 18, 37, 15, 7, 5, 9, 18, 24, 0] # repeated elements are removed num_list = [20, 18, 37, 15, 7, 5, 9, 18, 24, 0] # repeated elements are removed
list_tree = build_tree(num_list) list_tree = build_tree(num_list)
print(list_tree.InOrderTraversal()) # return list in sorted order print(list_tree.in_order_traversal()) # return list in sorted order
print(list_tree.PreOrderTraversal()) # return list in sorted order print(list_tree.pre_order_traversal()) # return list in sorted order
print(list_tree.PostOrderTraversal()) # return list in sorted order print(list_tree.post_order_traversal()) # return list in sorted order
# list_tree.display() # display tree using display function # list_tree.display() # display tree using display function
print(list_tree.search(20)) # True print(list_tree.search(20)) # True
print(list_tree.search(4)) # False print(list_tree.search(4)) # False
list_tree.delete(20) list_tree.delete(20)
print("Deleted element: ", list_tree.InOrderTraversal()) print("Deleted element: ", list_tree.in_order_traversal())
# String BST # String BST
country = ["India", "Australia", "France", "Japan", "Sweden"] country = ["India", "Australia", "France", "Japan", "Sweden"]
country_tree = build_tree(country) country_tree = build_tree(country)
print(country_tree.InOrderTraversal()) # return list in sorted order print(country_tree.in_order_traversal()) # return list in sorted order
print(country_tree.search("UK")) # False print(country_tree.search("UK")) # False
print(country_tree.search("Japan")) # True print(country_tree.search("Japan")) # True

View File

@ -1,65 +1,68 @@
# Author: github.com/Mo-Shakib # Author: github.com/Mo-Shakib
""" The binary tree data structure implementation """
class Node: class Node:
""" node class """
def __init__(self, data = None): def __init__(self, data = None):
""" initializing function """
self.left = None self.left = None
self.right = None self.right = None
self.data = data self.data = data
# for setting left node def set_left(self, node):
def setLeft(self, node): """ for setting left node """
self.left = node self.left = node
# for setting right node def set_right(self, node):
def setRight(self, node): """ for setting right node """
self.right = node self.right = node
# for getting the left node def get_left(self):
def getLeft(self): """ for getting the left node """
return self.left return self.left
# for getting right node def get_right(self):
def getRight(self): """ for getting right node """
return self.right return self.right
# for setting data of a node def set_data(self, data):
def setData(self, data): """ for setting data of a node """
self.data = data self.data = data
# for getting data of a node def get_data(self):
def getData(self): """ for getting data of a node """
return self.data return self.data
def inorder(tree):
""" in this we traverse first to the leftmost node,
then print its data and then traverse for rightmost node """
if tree:
inorder(tree.get_left())
print(tree.get_data(), end = ' ')
inorder(tree.get_right())
# in this we traverse first to the leftmost node, then print its data and then traverse for rightmost node def preorder(tree):
def inorder(Tree): """ in this we first print the root node
if Tree: and then traverse towards leftmost node and then to the rightmost node """
inorder(Tree.getLeft()) if tree:
print(Tree.getData(), end = ' ') print(tree.get_data(), end = ' ')
inorder(Tree.getRight()) preorder(tree.get_left())
return preorder(tree.get_right())
# in this we first print the root node and then traverse towards leftmost node and then to the rightmost node def postorder(tree):
def preorder(Tree): """ in this we first traverse to the leftmost node
if Tree: and then to the rightmost node and then print the data """
print(Tree.getData(), end = ' ') if tree:
preorder(Tree.getLeft()) postorder(tree.get_left())
preorder(Tree.getRight()) postorder(tree.get_right())
return print(tree.get_data(), end = ' ')
# in this we first traverse to the leftmost node and then to the rightmost node and then print the data
def postorder(Tree):
if Tree:
postorder(Tree.getLeft())
postorder(Tree.getRight())
print(Tree.getData(), end = ' ')
return
if __name__ == '__main__': if __name__ == '__main__':
root = Node(1) root = Node(1)
root.setLeft(Node(2)) root.set_left(Node(2))
root.setRight(Node(3)) root.set_right(Node(3))
root.left.setLeft(Node(4)) root.left.set_left(Node(4))
print('Inorder Traversal:') print('Inorder Traversal:')
inorder(root) inorder(root)