Preorder Binary Tree traversal Recursive method - python

I was trying to understand implementation of Binary Tree traversal (PreOrder). The non recursive approach was fine, but I am totally lost while trying to understand the recursive approach.
Code :
def preorder_print(self, start, traversal): """Root->Left->Right"""
if start:
traversal += (str(start.value) + "-")
traversal = self.preorder_print(start.left, traversal)
traversal = self.preorder_print(start.right, traversal)
return traversal
Binary Tree
8
/ \
4 5
/ \ \
2 1 6
My understanding is while reaching Node 2(8-4-2), left of that node 2 is None. So if start: condition would fail.
Below are my questions.
After node2.left is None, how node2.right is traversed? (because if start: condition fails)
After node1, how the logic moves to node5 which rootNode.right?
My understanding on recursion is poor, kindly help!

Watch this, see if this helps:
class Node(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def addleft(self,value):
self.left = Node(value)
def addright(self,value):
self.right = Node(value)
def preorder_print(self, start, traversal='', depth=0):
print( " "*depth, start.value if start else "None" )
if start:
traversal += (str(start.value) + "-")
print(' '*depth, "check left")
traversal = self.preorder_print(start.left, traversal, depth+1)
print(' '*depth, "check right")
traversal = self.preorder_print(start.right, traversal, depth+1)
return traversal
base = Node(8)
base.addleft(4)
base.left.addleft(2)
base.left.addright(1)
base.addright(5)
base.right.addright(6)
print( base.preorder_print( base ) )
Output:
8
check left
4
check left
2
check left
None
check right
None
check right
1
check left
None
check right
None
check right
5
check left
None
check right
6
check left
None
check right
None
8-4-2-1-5-6-

Related

Binary Tree Conditional Insert

I am trying to write a logic to insert a node into a binary tree.
The node looks like this
class BinTree:
def __init__(self, Id):
self.Id = Id
self.NodeCounter = 1
self.left = None
self.right = None
I need to insert a new node only if it doesnt exist in the tree but increment the counter if it exists already.
As of now, what im doing is whenever i get a new element to insert, i first search it in the binary tree, if the node is found i increment the NodeCounter by 1, otherwise I again start traversing from root node and then go and insert the new node
The problem here is that for every new node, i am traversing the tree twice which i dont want… And when i am trying to search and insert at the same time,the counters get messed because of recursion.
Is there a way I can achieve this?
Any tips would be appreciated
i first search it in the binary tree
...this would only be an efficient process if your binary tree is a binary search tree (BST). I'll assume that is what we are talking about.
if the node is found i increment the NodeCounter by 1, otherwise I again start traversing from root node and then go and insert the new node
Why would you start from the root again? When you did the search and didn't find the node, there was a last node that you visited. Just attach the new node to it.
Here is a possible implementation:
class Node:
def __init__(self, id):
self.id = id
self.nodeCounter = 1
self.left = None
self.right = None
class BinTree:
def __init__(self):
self.root = None
def add(self, id):
self.root = self.addrecur(self.root, id)
def addrecur(self, node, id):
if not node:
node = Node(id)
elif id == node.id:
node.nodeCounter += 1
elif id < node.id:
node.left = self.addrecur(node.left, id)
else:
node.right = self.addrecur(node.right, id)
return node
def __repr__(self):
return self.indented(self.root)
def indented(self, node, indent=""):
if not node:
return ""
return (self.indented(node.right, indent + " ")
+ f"{indent}{node.id} ({node.nodeCounter})\n"
+ self.indented(node.left, indent + " "))
tree = BinTree()
tree.add(4)
tree.add(2)
tree.add(3)
tree.add(2)
tree.add(6)
tree.add(5)
tree.add(5)
print(tree)
This outputs the tree in side-ways view (root at left) with the node count in parentheses:
6 (1)
5 (2)
4 (1)
3 (1)
2 (2)

Minimum depth of a binary tree

I'm trying to write code to find the minimum depth of a binary tree.
https://leetcode.com/problems/minimum-depth-of-binary-tree/
# Definition for a binary tree node.
# class TreeNode(object):
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution(object):
def minDepth(self, node):
if node is None:
return 0
else :
# Compute the depth of each subtree
lDepth = self.minDepth(node.left)
rDepth = self.minDepth(node.right)
return min(lDepth, rDepth) + 1
However, this solution does not work on some test cases, such as a highly unbalanced binary tree, which devolves to a linked list (ex [2, None, 3, None, 4, None, 5, None, 6]
The minimum depth is 5 (as None children do not count.) However, my solution returns 1, so it must be treating the left child of 2 as the minimum depth leaf node.
2
/ \
3
/ \
4
/ \
5
/ \
6
I'm at a loss, why does this solution not adequately address this use case?
I think its because of the min function. Starting from 2, your code checks the left which is 0, then checks right which is recursively checked and returns 4. However, you call min(0, 4)+1 which gives you an output of 1.
Your code stops recursion on None which is not a TreeNode, obviously, the term "depth" is undefined for such objects. Try to stop your recursion on so-called "leafs": nodes without children. Check out my solution for this problem:
def is_leaf(node: TreeNode):
return node.left is None and node.right is None
def min_depth(root: TreeNode):
if is_leaf(root):
return 1
not_none_children = (
child if child is not None
for child in (root.left, root.right)]
)
return min(min_depth(child) for child in not_none_children) + 1
#Avinash, thanks for ID'ing the edge case.
And the problem #kellyBundy https://leetcode.com/problems/minimum-depth-of-binary-tree/submissions/
class Solution(object):
def minDepth(self, node):
# no node (dead branch)
if node is None:
return 0
lDepth = self.minDepth(node.left)
rDepth = self.minDepth(node.right)
if node.left and node.right:
return min(lDepth, rDepth) + 1
else: #prevents counting first dead branch as minDepth
return max(lDepth, rDepth) + 1

Can't understand this Binary Search Tree (BST) algorithm for Insertion of value in Python

I am learning algorithms. For the BST Construction to insert a value in the BST, I came across this code. I am not that good at OOPS concept and can't figure out how currentnode.left = BST(value) and currentnode = currentnode.left works. If someone can help me understand this, it would be of great help.
class BST:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def insert(self, value):
currentnode = self
while True:
if value < currentnode.value:
if currentnode.left is None:
currentnode.left = BST(value)
break
else:
currentnode = currentnode.left
else:
if currentnode.right is None:
currentnode.right = BST(value)
break
else:
currentnode = currentnode.right
return self
In the insert function, the currentnode has been assigned to self. Starting with the while loop, the parameter value is checked with currentnode's value. If it is small, the first condition is executed else second.
Now comes your doubt.
Let's say the first condition is being executed. If the currentnode's left value is none, then the code calls BST(value) i.e. the constructor is called which initiates a new node, which in turn becomes the currentnode's left child.
Else, if there is already a left child, that child becomes the currentnode and the while loop is iterated again and again until a suitable, position is found.
Also, If this code seems complicated. You should refer this, just in case if it helps.
class Node:
def __init__(self,key):
self.left = None
self.right = None
self.val = key
def insert(root,node):
if root is None:
root = node
else:
if root.val < node.val:
if root.right is None:
root.right = node
else:
insert(root.right, node)
else:
if root.left is None:
root.left = node
else:
insert(root.left, node)
# 5
# / \
# 3 7
# / \ / \
# 2 4 6 8
r = Node(5)
insert(r,Node(3))
insert(r,Node(2))
insert(r,Node(4))
insert(r,Node(7))
insert(r,Node(6))
insert(r,Node(8))

Successor and Predecessor - Binary Search Tree (Python)

I am trying out this successor and predecessor on Binary search tree.
Just wondering once I get hold of successor code, can I flip it over and use it for predecessor?
I've coded the successor, and tried to use it for predecessor.
However, my output value doesn't change, even though I tried to place other value..
Below is my code:
Succ
def succ(self, key):
temp = self.root
prev = None
if (temp.right is not None):
temp = temp.right
while (temp.left is not None):
temp = temp.left
prev = temp
elif temp.left is not None:e
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
else:
return None
return prev.key
Predecessor
def pred(self, key):
temp = self.root
prev = None
#if right is not none, succ lies in the right sub tree
if (temp.right is not None):
temp = temp.right
while (temp.left is not None):
#return the node with the minimum key value
temp = temp.left
prev = temp
#if left not none, succ lies in the right sub tree
elif temp.left is not None:
#go right till .right is None
#return the node with the maximum key value
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
else:
#no succ
return None
return prev.key
the tree
def createTree(self):
#root
self.put("F",6)
#leftSubTree
self.put("D",4)
#leftLeftSubTree
self.put("C",3)
self.put("B",2)
self.put("A",1)
#LeftRightSubTree
self.put("E",5)
#RightSubTree
self.put("I",9)
#RightLeftSubTree
self.put("G",7)
self.put("H",8)
#RightRightSubTree
self.put("J",10)
To flip the succ function and turn it into pred, you need to change every left to a right, and every right to a left.
def pred(self, key):
temp = self.root
prev = None
if (temp.left is not None):
temp = temp.left
while (temp.right is not None):
temp = temp.right
prev = temp
elif temp.right is not None:
temp = temp.right
while (temp.left is not None):
temp = temp.left
prev = temp
else:
return None
return prev.key
Let us assume you have a BST node class with three pointers/references: left, right, and parent, which correspond to the left child, right child, and parent of a given node. The only node in the tree which has a parent that points to None would be the root node.
Let us also assume that we have the following BST:
15
/ \
9 20
/ \ / \
3 10 17 21
/ \ \
1 5 11
The BST property states that for any given node n, all nodes in n's left sub-tree shall be less than n; and, all nodes in n's right sub-tree shall be greater than n.
To make things easier when implementing the successor and predecessor functions, it is helpful to have auxiliary functions for finding the minimum and maximum node of a given BST or BST sub-tree.
Minimum
def bst_minimum(tree):
minimum = tree
while minimum is not None:
minimum = minimum.left
return minimum
Maximum
def bst_maximum(tree):
maximum = tree
while maximum is not None:
maximum = maximum.right
return maximum
For the tree example above, these functions would return 1 for the minimum and 21 for the maximum.
To find the predecessor of a given node, you have to cover a few cases:
If the given node has a left sub-tree, then take the maximum of that sub-tree.
Otherwise, move up the tree, following parent nodes until either you hit None or you "turn left."
In the second case, if you hit None, that means there is no predecessor. This would be the case for the node with value 1 in the tree above. It would following parent pointers all the way past the root node.
If there is a predecessor, then it will be the first parent node you encounter after making a left turn up the tree. Put another way, it is the parent node whose value is less than the value of the node from which you started. So, node 17 above would return the root node with a value of 15.
Predecessor
def bst_predecessor(tree):
if tree.left is not None:
return bst_maximum(tree.left)
parent = tree.parent
child = tree
while parent is not None and child is parent.left:
child = parent
parent = child.parent
return parent
Since the successor is simply the symmetrical operation to predecessor, you can modify predecessor by flipping the various operations. Namely:
Instead of checking the left sub-tree and finding the maximum, you want to check the right tree and find its minimum.
Otherwise, follow parent nodes until you can't anymore (in which case there is no successor), or you turn right. So, the successor of node 5, would be node 9 in the tree above.
Successor
def bst_successor(tree):
if tree.right is not None:
return bst_minimum(tree.right)
parent = tree.parent
child = tree
while parent is not None and child is parent.right:
child = parent
parent = child.parent
return parent

Implementing Binary Search Tree (Python)

I have the task to perform some basic operations on Binary Search Trees and I'm not sure what is the clever way to do it.
I know that the usual way would be to write a class for the nodes and one for the tree so that I can build up my tree from given values and perform certain tasks on it. The thing is, I'm already getting the tree as a list and since BSTs are not unique, there won't come any good from it if I take each value and build the tree myself.
So... I'm getting a list like this:
11 9 2 13 _, 4 18 2 14 _, 2 10 _ 11 4, 14 16 4 _ _, 13 0 11 _ _ | 10 | 7
which means:
key value parent left right, ... | value1 | value2
So as you see the BST is given explicitly. My tasks are to do a level-print of the tree, return the path from root to value1, do a rotate-right operation on the subtree that has value1, then delete value1 and then insert value2.
What would be an efficient way to tackle this problem?
Here is one possible way of implementing the tree. Hope it helps. Though this contains insertions and popular traversals, not rotations or deletions.
Reference: http://www.thelearningpoint.net/computer-science/learning-python-programming-and-data-structures/learning-python-programming-and-data-structures--tutorial-20--graphs-breadth-and-depth-first-search-bfsdfs-dijkstra-algorithm-topological-search
'''
Binary Search Tree is a binary tree(that is every node has two branches),
in which the values contained in the left subtree is always less than the
root of that subtree, and the values contained in the right subtree is
always greater than the value of the root of the right subtree.
For more information about binary search trees, refer to :
http://en.wikipedia.org/wiki/Binary_search_tree
'''
#Only for use in Python 2.6.0a2 and later
from __future__ import print_function
class Node:
# Constructor to initialize data
# If data is not given by user,its taken as None
def __init__(self, data=None, left=None, right=None):
self.data = data
self.left = left
self.right = right
# __str__ returns string equivalent of Object
def __str__(self):
return "Node[Data = %s]" % (self.data,)
class BinarySearchTree:
def __init__(self):
self.root = None
'''
While inserting values in a binary search tree, we first check
whether the value is greater than, lesser than or equal to the
root of the tree.
We initialize current node as the root.
If the value is greater than the current node value, then we know that
its right location will be in the right subtree. So we make the current
element as the right node.
If the value is lesser than the current node value, then we know that
its right location will be in the left subtree. So we make the current
element as the left node.
If the value is equal to the current node value, then we know that the
value is already contained in the tree and doesn't need to be reinserted.
So we break from the loop.
'''
def insert(self, val):
if (self.root == None):
self.root = Node(val)
else:
current = self.root
while 1:
if (current.data > val):
if (current.left == None):
current.left = Node(val)
break
else:
current = current.left
elif (current.data < val):
if (current.right == None):
current.right = Node(val)
break
else:
current = current.right
else:
break
'''
In preorder traversal, we first print the current element, then
move on to the left subtree and finally to the right subree.
'''
def preorder(self, node):
if (node == None):
return
else:
print(node.data, end=" ")
self.preorder(node.left)
self.preorder(node.right)
'''
In inorder traversal, we first move to the left subtree, then print
the current element and finally move to the right subtree.
'''
#Important : Inorder traversal returns the elements in sorted form.
def inorder(self, node):
if (node == None):
return
else:
self.inorder(node.left)
print(node.data, end=" ")
self.inorder(node.right)
'''
In postorder traversal, we first move to the left subtree, then to the
right subtree and finally print the current element.
'''
def postorder(self, node):
if (node == None):
return
else:
self.postorder(node.left)
self.postorder(node.right)
print(node.data, end=" ")
tree = BinarySearchTree()
tree.insert(1)
tree.insert(9)
tree.insert(4)
tree.insert(3)
tree.insert(5)
tree.insert(7)
tree.insert(10)
tree.insert(0)
print ("Preorder Printing")
tree.preorder(tree.root)
print("\n\nInorder Printing")
tree.inorder(tree.root)
print("\n\nPostOrder Printing")
tree.postorder(tree.root)
Here is the implementation of Binary Search Tree with it's basic operations like insert node, find node
class Node:
def __init__(self,data):
self.left = None
self.right = None
self.data = data
class BST:
def __init__(self):
self.root = None
def set_root(self,data):
self.root = Node(data)
def insert_node(self,data):
if self.root is None:
self.set_root(data)
else:
n = Node(data)
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
else:
troot.left = n
break
else:
if troot.right:
troot = troot.right
else:
troot.right = n
break
def search_node(self,data):
if self.root is None:
return "Not found"
else:
troot = self.root
while troot:
if data < troot.data:
if troot.left:
troot = troot.left
if troot.data == data:
return "Found"
else:
return "Not found"
elif data > troot.data:
if troot.right:
troot = troot.right
if troot.data == data:
return "Found"
else:
return "Not found"
else:
return "Found"
tree = BST()
tree.insert_node(10)
tree.insert_node(5)
tree.insert_node(20)
tree.insert_node(7)
print(tree.root.data)
print(tree.root.left.data)
print(tree.root.right.data)
print(tree.root.left.right.data)
print(tree.search_node(10))
print(tree.search_node(5))
print(tree.search_node(20))
print(tree.search_node(7))
print(tree.search_node(12))
print(tree.search_node(15))
Output:
10
5
20
7
Found
Found
Found
Found
Not found
Not found
In this specific case I had success using a dictionary as a datatype to store the graph. The key is the node_key and the value is a list with the attributes of the node. In this way it is rather fast to find the needed nodes and all its attributes.
I'm just not sure if there is a way to make it reasonably faster.

Categories