Lecture 1 (Binary tree traversals)¶
Binary Tree Representation¶
In [2]:
class Node:
def __init__(self,val=0,left=None,right=None):
self.val = val
self.left = left
self.right = right
Convert an array to Tree¶
- BFS apprach
In [7]:
from collections import deque
def to_tree(nums):
n = len(nums)
if n ==0:
return None
root = Node(nums[0])
queue = deque([root])
idx =1
while queue:
node = queue.popleft()
# left child
if idx <n:
val = nums[idx]
if val !=-1:
node.left = Node(val)
queue.append(node.left)
idx +=1
# right child
if idx <n:
val = nums[idx]
if val != -1:
node.right = Node(val)
queue.append(node.right)
idx +=1
return root
root = to_tree([1,2,3,-1,5])
print(root)
<__main__.Node object at 0x111354c90>
Preorder traversal of binary tree¶
input = [4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1]
output = [4,2,3,9,1,5,7,6,8]
input = [1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10]
output = [1,2,4,5,8,3,6,7,9,10]
Approach 1(Recursion)¶
- pre order (Root , Left , Right)
In [12]:
def pre_order_traversal(root):
result = []
def visit(node):
if node is None:
return
result.append(node.val)
visit(node.left)
visit(node.right)
visit(root)
return result
print(pre_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(pre_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[4, 2, 3, 9, 1, 5, 7, 6, 8] [1, 2, 4, 5, 8, 3, 6, 7, 9, 10]
Complexity¶
- N = nodes count
O(N)
O(N)
Approach 2 (Iteration)¶
- stack
- push right and then left
In [53]:
def pre_order_traversal(root):
result = []
stack = [root]
while stack:
root = stack.pop()
if root == None:
continue
result.append(root.val)
stack.append(root.right)
stack.append(root.left)
return result
print(pre_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(pre_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[4, 2, 3, 9, 1, 5, 7, 6, 8] [1, 2, 4, 5, 8, 3, 6, 7, 9, 10]
Complexity¶
- N : no. of nodes in tree
- O(N)
- O(N)
Inorder Traversal of Binary Tree¶
Input = [4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1]
Output = [3,1,9,2,4,7,5,8,6]
Input = [1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10]
Output = [4,2,8,5,1,6,3,9,7,10]
Approach 1 (Recursive)¶
- In order : (Left , Root , Right)
In [14]:
def in_order_traversal(root):
result = []
def visit(node):
if node is None:
return
visit(node.left)
result.append(node.val)
visit(node.right)
visit(root)
return result
print(in_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(in_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[3, 1, 9, 2, 4, 7, 5, 8, 6] [4, 2, 8, 5, 1, 6, 3, 9, 7, 10]
Complexity¶
- N = nodes count
O(N)
O(N)
Approach 2 (Iteration)¶
- Stack
- left root right => right root left
In [56]:
def in_order_traversal(root):
result = []
stack = []
while stack or root:
#go to left first
while root:
stack.append(root)
root = root.left
root = stack.pop()
result.append(root.val)
root = root.right
return result
print(in_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(in_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[3, 1, 9, 2, 4, 7, 5, 8, 6] [4, 2, 8, 5, 1, 6, 3, 9, 7, 10]
Post-Order Traversal Of Binary Tree¶
input= [4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1]
output= [1,9,3,2,7,8,6,5,4]
input= [1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10]
output= [4,8,5,2,6,9,10,7,3,1]
Approach (Recursive)¶
- Post Order (Left Right Root)
In [16]:
def post_order_traversal(root):
result = []
def visit(node):
if node is None:
return
visit(node.left)
visit(node.right)
result.append(node.val)
visit(root)
return result
print(post_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(post_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[1, 9, 3, 2, 7, 8, 6, 5, 4] [4, 8, 5, 2, 6, 9, 10, 7, 3, 1]
Complexity¶
- N = nodes count
O(N)
O(N)
Approach 2 (Iterative)¶
- Use 2 stacks
- first stack : for traversal
- second stack for post order result (reversed order)
- Post Order = Left Right Root
- stack1 = [root]
- stack2 = []
- ROOT LEFT RIGHT (On stack1) => Root (push it to stack2)
- result = rever(stack2)
In [60]:
def post_order_traversal(root):
if not root:
return []
result = []
stack1 = [root] # for traversal
stack2= [] # result in reveresed order
while stack1:
root = stack1.pop()
stack2.append(root)
if root.left:
stack1.append(root.left)
if root.right:
stack1.append(root.right)
while stack2:
root = stack2.pop()
result.append(root.val)
return result
print(post_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(post_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[1, 9, 3, 2, 7, 8, 6, 5, 4] [4, 8, 5, 2, 6, 9, 10, 7, 3, 1]
Level Order Traversal of a Binary Tree¶
Input = [4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1]
Ouput = [[4],[2,5],[3,7,6],[9,8],[1]]
Input = [1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10]
Output = [[ [1],[2, 3],[4, 5, 6, 7],[8, 9, 10]]]
Approach 1 (BFS)¶
- We need to loop till the queue size (for each level)
In [19]:
from collections import deque
def level_order_traversal(root):
result = []
queue = deque([root])
while queue:
node_count = len(queue)
values = []
for _ in range(node_count):
node = queue.popleft()
values.append(node.val)
if node.left:
queue.append(node.left)
if node.right:
queue.append(node.right)
result.append(values)
return result
print(level_order_traversal(to_tree([4,2,5,3,-1,7,6,-1,9,-1,-1,8,-1,1])))
print(level_order_traversal(to_tree([1,2,3,4,5,6,7,-1,-1,8,-1,-1,-1,9,10])))
[[4], [2, 5], [3, 7, 6], [9, 8], [1]] [[1], [2, 3], [4, 5, 6, 7], [8, 9, 10]]
Complexity¶
N = number of nodes in a tree
O(N)
O(N)