The reason this doesn't work is that res only has the value of the first node you give it appended to it; each time you recursively recall the function, it just makes a new res. It is a simple fix though, as follows:
class Solution(object):
def inorderTraversal(self, root):
res = []
if root:
res = self.inorderTraversal(root.left)
res.append(root.val)
res = res + self.inorderTraversal(root.right)
return res
In this, it returns the left branch, the value, and then the right. This can be done much more briefly as follows:
class Solution(object):
def inorderTraversal(self, root):
return (self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)) if root else []
Answer from Benedict Randall Shaw on Stack Overflowlist - Inorder Binary Tree Traversal (using Python) - Stack Overflow
Traversing a binary tree in Python - Stack Overflow
Binary Tree Traversal Using Recursion Explanation (python) - Stack Overflow
4 Ways to Traverse a Binary Tree
The reason this doesn't work is that res only has the value of the first node you give it appended to it; each time you recursively recall the function, it just makes a new res. It is a simple fix though, as follows:
class Solution(object):
def inorderTraversal(self, root):
res = []
if root:
res = self.inorderTraversal(root.left)
res.append(root.val)
res = res + self.inorderTraversal(root.right)
return res
In this, it returns the left branch, the value, and then the right. This can be done much more briefly as follows:
class Solution(object):
def inorderTraversal(self, root):
return (self.inorderTraversal(root.left) + [root.val] + self.inorderTraversal(root.right)) if root else []
Use this instead , a simple recursion ::
class Node:
def __init__(self,key):
self.left = None
self.right = None
self.val = key
def printInorder(root):
if root:
printInorder(root.left)
print(root.val)
printInorder(root.right)
def printPostorder(root):
if root:
printPostorder(root.left)
printPostorder(root.right)
print(root.val)
def printPreorder(root):
if root:
print(root.val)
printPreorder(root.left)
printPreorder(root.right)
# Driver code
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print "Preorder traversal of binary tree is"
printPreorder(root)
print "\nInorder traversal of binary tree is"
printInorder(root)
print "\nPostorder traversal of binary tree is"
printPostorder(root)
Source :: here
There is another solution for recursive in-order tree traversal using Python 3's yield from:
def traverse(tree):
if tree.left is not None:
yield from traverse(tree.left)
yield tree.value
if tree.right is not None:
yield from traverse(tree.right)
print(list(traverse(tree_root)))
I think it's far more readable and conceptually simple. I hope it will be helpful to somebody.
All you need to do is create a helper method visitAllSubnodes(node) which does something to the current node, then recursively calls itself on the left and right subnodes. What visitAllSubnodes(node) does can be anything, in your case it could be something like print(node._element), but you can make your function wonderfully modular, e.g.
def visitAllSubnodes(node, whatToDoAtEachNode):
whatToDoAtEachNode(node)
visitAllSubnodes(node._left, whatToDoAtEachNode)
visitAllSubnodes(node._right, whatToDoAtEachNode)
def printAllElements(node):
visitAllSubnodes(node, lambda x:print(x))
To actually return something, you need to use the concept of higher order functions and closures. For example, you could make a function which defines a private personal accumulator (a list you add to), and another function to which that private personal accumulator belongs to, then return the function.
So for example, each time traversed the tree, you could invoke this higher order function, let's call it makeFunctionWhichKeepsTrackOfWhatHasBeenPassedIntoIt(), which returns a function that keeps track of what has been passed into it, as well as the accumulator. I'd give more info but that would be the point of the problem set. =)
One useful tool for understanding the path that an algorithm takes is to add logging to it.
def inorder_print(self, start, traversal, indent=""):
"""Left -> root -> right"""
if start:
print(f"{indent} {start.data} <- '{traversal}'")
traversal = self.inorder_print(start.left, traversal, indent+" ")
traversal += (str(start.data) + "-")
print(f"{indent} {start.data} -- '{traversal}'")
traversal = self.inorder_print(start.right, traversal, indent+" ")
print(f"{indent} {start.data} -> '{traversal}'")
return traversal
allows us to visualize the tree and the order in which each node is added to the traversal:
1 <- ''
2 <- ''
4 <- ''
4 -- '4-'
4 -> '4-'
2 -- '4-2-'
5 <- '4-2-'
5 -- '4-2-5-'
5 -> '4-2-5-'
2 -> '4-2-5-'
1 -- '4-2-5-1-'
3 <- '4-2-5-1-'
6 <- '4-2-5-1-'
6 -- '4-2-5-1-6-'
6 -> '4-2-5-1-6-'
3 -- '4-2-5-1-6-3-'
7 <- '4-2-5-1-6-3-'
7 -- '4-2-5-1-6-3-7-'
8 <- '4-2-5-1-6-3-7-'
8 -- '4-2-5-1-6-3-7-8-'
8 -> '4-2-5-1-6-3-7-8-'
7 -> '4-2-5-1-6-3-7-8-'
3 -> '4-2-5-1-6-3-7-8-'
1 -> '4-2-5-1-6-3-7-8-'
The indentation shows the depth of the stack. Each individual recursive call has three parts -- the left child, self.data, and the right child.
The code "knows" that 2 goes after 4 because 4 happens at the start of 2's call, and the string '2-' is appended immediately afterward:
2 <- '' # start of 2's call. 2 starts by calling 4
4 <- '' # start of 4's call
4 -- '4-' # 4 appends self.data
4 -> '4-' # end of 4's call, returning to 2
2 -- '4-2-' # 2 appends self.data to what it got from 4, and calls 5
5 <- '4-2-' # start of 5's call
5 -- '4-2-5-' # 5 appends self.data
5 -> '4-2-5-' # end of 5's call, returning to 2
2 -> '4-2-5-' # end of 2's call
Ok, sat with it a little longer and figured it out. Thought id post my thoughts so that this question can be marked as answered.
- inorder func is called with root and empty string.
- start has value of tree.root, while start has value it keeps recursing at traversal = self.inorder_print(start.left, traversal)
- it is to be noted, that each time a recursion happens you go further into the tree. start.data = 1. recursion, start.data = 2 recursion, start.data = 4. due to the fact that 4 does not have a left child. the function can finally return. start.data now = 4 and the next line traveral += str(start.data) + "-") can run, adding to the string. However remember that the function is still 2 recurrsions deep. our start.data is back to 2. as that function has finished executing, 2 gets added to traversal via line traveral += str(start.data) + "-")/ #recursion happens with start.right. start.data now =5 start.right has no value, so the function can return 5. this process continues for the whole tree, until all of the functions that have been executed have been completed. the trick to undertanding (at least for me) is that the when you return to a higher level in recursion, the function starts off where it left and doenst start the function from the beginning.
Brush up with Tree Data Structure
Here's a Binary Search Tree (BST). Every circle is called a node and each node can be connected to 2 other nodes -- one on the left and right. That's why they're called Binary Trees, they have 2 "child" nodes and it looks like a tree!
A Binary TreeThe left child node is always smaller or equal in value than the parent, and the right is always greater or equal. Some implementations allow only the left or right node to be equal to the parent. Personally, I like putting equal nodes, to the left side ๐
BTW, here's a Binary Search Tree using python3!
class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = rightThe default values for the left and right tree are null/None and default val is 0.
Traversals
For each traversal I'm going to give a brief description of how it moves through the binary tree, starting from the root (the top). Then show the code for traversal using python and lastly a GIF visualizing how the program moves through the tree! Also all of the gifs will end early without going through the entire tree because the gif sizes were getting too big and I had to cut it short ๐คทโโ๏ธ.
The first 3 are very similar and only differ by one line of code but give very different results! I call them the traversal triplets.
In Order
This method moves through the tree "in order". Meaning that it will print every nodeโs value in order from smallest to greatest.
Using recursion, the function will call itself on the left sub-tree, then print the current value, and then call itself on the right sub-tree. The result is that it prints the tree's values.... in order.
def in_order(node):
if node == None:
return
in_order(node.left)
print(node.val)
in_order(node.right)
In Order Traversal Animation
Depth-First Search (also known as pre-order)
Depth-First Search is the classic traversal method for trees and also graphs. This method is also known as the pre-order traversal because it first operates on the current node, then the left and right child nodes.
Depth first search is very popular for general traversal as well as making copies of the tree.
def dfs(node):
if node == None:
return
print(node.val)
dfs(node.left)
dfs(node.right)
DFS Traversal Animation
Post Order
The last of the traversal triplets. It first operates on the left and right child nodes then the current node.
Post Order is very useful for deleting a tree, as it goes from the bottom up.
def post_order(node):
if node == None:
return
post_order(node.left)
post_order(node.right)
print(node.val)
Post Order Traversal Animation
Breadth-First Search (also Level Order)
This one is a little more tricky and can only be done using a queue. It might be possible with recursion but it's easier to understand iteratively. It uses a queue when traversing so it goes through the tree as nodes are added to it. As a result, it goes through the tree, level by level. This makes breadth-first search a popular search algorithm in graphs as well.
Although I couldn't make an animation for this one, I created a numbered diagram to show what order the nodes would be printed!
# deque is a python queue library
from collections import deque
def bfs(root):
q = deque([root])
while len(q) > 0:
node = q.popleft()
print(node.val)
if node.left:
q.append(node.left)
if node.right:
q.append(node.right)
Numbered Level Order Traversal
Conclusion
By understanding these 4 traversal methods, you can demystify most interview problems involving binary trees. A lot of them can be broken down into easily solvable chunks if you know the right traversal method to use! If you're curious about the exact methodology, I've got a guide coming out soon! I hope it'll be the only resource needed for solving binary tree problems.
Also, all animations were courtesy of http://btv.melezinek.cz/binary-search-tree.html. Make sure to check them out if you want to play around with the animations!
By The Way I'm Making a Guide
I'm making a guide on how to solve almost any Binary Tree problem on LeetCode (and therefore in an interview)! Join me on my journey by following me on Twitter!