Zigzag level order traversal - python

I am trying to do the zigzag level order traversal of a binary tree's nodes values (ie, from left to right, then right to left for the next level and alternate between) on https://www.interviewbit.com/problems/zigzag-level-order-traversal-bt/ But the compiler gives time limit exceeded error. How can I resolve it?
# Definition for a binary tree node
# class TreeNode:
# def __init__(self, x):
# self.val = x
# self.left = None
# self.right = None
class Solution:
# #param A : root node of tree
# #return a list of list of integers
def zigzagLevelOrder(self, A):
st_crt_lvl =[A]
st_nxt_lvl =[]
ans_list = []
while st_crt_lvl:
u = st_crt_lvl.pop(0)
ans_list.append(u.val)
if u.left:
st_nxt_lvl.append(u.left)
if u.right:
st_nxt_lvl.append(u.right)
while st_nxt_lvl:
u = st_nxt_lvl.pop()
ans_list.append(u.val)
if u.right:
st_crt_lvl.append(u.right)
if u.left:
st_crt_lvl.append(u.left)
return ans_list

You can eliminate multiple inner while loops from your code, by making the queue st_nxt_lvl temporary and copying the content of this temporary queue to the current one st_crt_lvl at the end processing of each level of the binary tree.
This could be achieved by keeping just a single queue (without any temporary storage) and level order traversal by standard bfs algorithm, but since we want zig-zag order traversal at each level, it's more elegant to have a temporary queue, so that the temporary queue only keeps the next level elements and when processing of the current level elements is done, the current queue points to the next level.
With some modification of your code, along with an example tree:
def zigzagLevelOrder(A):
st_crt_lvl = [A] # initialize
ans_list = []
level = 0
while st_crt_lvl: # check if all levels are processed
st_nxt_lvl =[] # temporary queue to hold the next level elements
tmp = [] # temporary storage to append to the ans_list
while st_crt_lvl:
u = st_crt_lvl.pop(0)
tmp.append(u.val)
if u.left:
st_nxt_lvl.append(u.left)
if u.right:
st_nxt_lvl.append(u.right)
if (len(tmp) > 0): # if tmp is not empty
if level % 2 == 1: # ensure zig-zag level order traversal
tmp = tmp[::-1]
ans_list.append(tmp)
st_crt_lvl = st_nxt_lvl # copy the temporary queue to the current queue
level += 1
return ans_list
class BinaryTree:
def __init__(self, left, right, data):
self.left = left
self.right = right
self.val = data
A = BinaryTree(None, None, 3)
A.left = BinaryTree(None, None, 9)
A.right = BinaryTree(None, None, 20)
A.left.left = BinaryTree(None, None, 1)
A.left.right = BinaryTree(None, None, 2)
A.right.left = BinaryTree(None, None, 15)
A.right.right = BinaryTree(None, None, 7)
zigzagLevelOrder(A)
# [[3], [20, 9], [1, 2, 15, 7]]

You can use a breadth-first search:
from collections import deque, defaultdict
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['left', 'right', 'value']}
def __contains__(self, _val):
if self.value != _val and self.left is None and self.right is None:
return False
return True if self.value == _val else any([_val in [[], self.left][self.left is not None], _val in [[], self.right][self.right is not None]])
def __lookup(self, _val, _depth = 0):
if self.value == _val:
return _depth
return getattr(self, 'left' if _val in self.left else 'right').__lookup(_val, _depth+1)
def __getitem__(self, _val):
return self.__lookup(_val)
def bfs(_head):
_d = deque([_head])
_seen = []
_last = None
_final_result = defaultdict(list)
while _d:
_current = _d.popleft()
if _current.value not in _seen:
_seen.append(_current.value)
_r = list(filter(None, [getattr(_current, i, None) for i in ['left', 'right']]))
_d.extend(_r)
_final_result[_head[_current.value]].append(_current.value)
marker = iter(range(len(_final_result)))
return [i[::-1] if next(marker)%2 else i for _, i in sorted(_final_result.items(), key=lambda x:x[0])]
When constructing the same tree in the example from the link in the question, the output is:
t = Tree(value=3, left=Tree(value=9), right=Tree(value=20, left=Tree(value=15), right=Tree(value=7)))
print(bfs(t))
Output:
[[3], [20, 9], [15, 7]]
This solution uses the breadth-first search to traverse the tree horizontally. However, since a (complete) binary tree has two children, only two values will be added to the queue. Therefore, in order to determine the current tree level the search has reached, a method to find the depth of a particular value has to be implemented in the tree (__getitem__ and __lookup).

Related

Convert binary tree to a list with level order in Python

I have not found any related questions yet.
So the problem is: given an incomplete binary tree, say the root of the tree, how can I convert it into a list in level order in Python such that the empty nodes (missing nodes in that level) are represented as "None" in the list.
For example, I have this tree:
1
/ \
4 0.52
/ \ / \
2.5
I want to get the following list:
[1, 4, 0.52, None, 2.5, None, None]
by using some function like:
list = toList(root)
In addition, I have tree structured like this:
class TreeNode:
def __init__(self, value, isLeaf):
self.left = None
self.right = None
self.value = value
self.isLeaf = isLeaf
And borrow the solution from another post (to a list without 'None' taken place):
def toList(root):
node_list = []
thislevel = [root]
while thislevel:
nextlevel = list()
for n in thislevel:
# print(n.value)
node_list.append(n.value)
if n.left:
nextlevel.append(n.left)
if n.right:
nextlevel.append(n.right)
# print
thislevel = nextlevel
return node_list
my_list = toList(root)
print(my_list)
This gives the following so far:
[1, 4, 0.52, 2.5]
I am stuck here, don't know how to properly insert 'None' into the list...
Thanks!
You typically make a breadth-first iteration of a graph by using a queue. This is a first-in, first-out data structure. You start by adding the root to the queue, and then while the queue has items in it, you pop out the oldest one, add it to the results and the push its children into the queue. If you are doing this for anything other than small input, python’s collections.deque is more efficient than the list used here:
class TreeNode:
def __init__(self, value, isLeaf):
self.left = None
self.right = None
self.value = value
self.isLeaf = isLeaf
def breadth_first(t):
q = [t]
while q:
current = q.pop(0)
yield current.value if current else None
if current and not current.isLeaf:
q.extend([current.left, current.right])
t = TreeNode(1, False)
t.left = TreeNode(4, False)
t.right = TreeNode(0.52, False)
t.left.right = TreeNode(0.2
list(breadth_first(t))
# [1, 4, 0.52, None, 0.25, None, None]

How do I make TreeNode from list by Python

Lately, I started "leetcode" for studying programming. Sometimes, I encounter the question which is related to TreeNode.
https://leetcode.com/problems/longest-univalue-path/
I usually run code in local to make sure if my code work. But those questions require me to prepare for TreeNode in advance, otherwise, I can not run in local. I don't know how to build TreeNode from a list.
I want to make TreeNode from a list by Python, like here.
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
input: [5,4,5,1,1,5]
output:
TreeNode{val: 5, left: TreeNode{val: 4, left: TreeNode{val: 1, left: None, right: None}, right: TreeNode{val: 1, left: None, right: None}}, right: TreeNode{val: 5, left: TreeNode{val: 5, left: None, right: None}, right: None}}
I know we can make sure whether the code work or not on leetcode. However, I think it's slow for me to check the code on leetcode. I would like to run my code in local. I hope you will help me.
Take a look at LeetCode's official explanation https://support.leetcode.com/hc/en-us/articles/360011883654-What-does-1-null-2-3-mean-in-binary-tree-representation- of how their serialized formatting of a binary tree into the kind of list you see in their test cases works. If you want to run your solution against those test cases locally, you'll also need to write some code (or I'm sure you can find some online) that will input a serialized list, build the tree, and return the tree's root TreeNode so you can pass it to your find_longest_univalue_path function.
Guess this is what you need:
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def creatBTree(data, index):
pNode = None
if index < len(data):
if data[index] == None:
return
pNode = TreeNode(data[index])
pNode.left = creatBTree(data, 2 * index + 1) # [1, 3, 7, 15, ...]
pNode.right = creatBTree(data, 2 * index + 2) # [2, 5, 12, 25, ...]
return pNode
Say you are cracking pathSum, populate the tree by calling
lst = [5,4,8,11,None,13,4,7,2,None,None,None,1]
root = creatBTree(lst, 0)
Here is just beautified StefanPochmann's solution which was described at
LeetCode's Help Center
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
return 'TreeNode({})'.format(self.val)
def deserialize(string):
if string == '[]':
return None
nodes = [None if val == 'null' else TreeNode(int(val))
for val in string.strip('[]').split(',')]
kids = nodes[::-1]
root = kids.pop()
for node in nodes:
if node:
if kids:
node.left = kids.pop()
if kids:
node.right = kids.pop()
return root
if __name__ == '__main__':
tree = deserialize('[3,9,20,null,null,15,7]')
assert tree == TreeNode(3, TreeNode(9), TreeNode(20, TreeNode(15), TreeNode(7)))
Here's a drop-in replacement for the incomplete class LeetCode provides in questions such as https://leetcode.com/problems/invert-binary-tree/
import json
class TreeNode:
def __init__(self, val, left=None, right=None):
self.val = val
self.left = left
self.right = right
def __repr__(self):
# Shrink the long json string by removing unnecessary lines
t = '\n'.join([l for l in self.json_repr.split('\n')
if '[' not in l and ']' not in l])
return t.replace(',','')
#property
def json_repr(self):
if self.val is None:
return 'None'
# Recursively construct a json-compliant representation
if self.left is None:
text = f"[{self.val}]"
else:
text = f"[{self.val}, [{self.left.json_repr}, {self.right.json_repr}]]"
return json.dumps(json.loads(text), indent=1)
def from_list(l):
nodes = [TreeNode(v) for v in l]
kids = nodes[::-1]
root = kids.pop()
for node in nodes:
if node:
if kids:
node.left = kids.pop()
if kids:
node.right = kids.pop()
return root
t = TreeNode.from_list([4,2,7,1,3,6,9])
print(t)
Returns:
4
2
1
3
7
6
9

Binary Tree Level Order Traversal in Python

Given the following tree:
I should return the level order traversal of the tree from left to right:
so the above example would output a list of lists :
[ [3], [9,20], [15,7] ]
I wrote the following code, idea is storing the node value and its depth recursively in a Queue then iterate the Queue tuples and put the in intermediate list O if no more node of same depth append O to output and empty O and so on. However my code Timeout any help?
import queue
class Solution:
def levelOrder(self, root: TreeNode) -> List[List[int]]:
def helper(root,res,level):
if not root:
return res
l=level+1
res.put((root.val,level))
helper(root.left,res,l)
helper(root.right,res,l)
res=queue.Queue()
helper(root,res,0)
d=1
output=[]
node,depth=res.get()
output.append([node])
while res:
o=[]
node,depth=res.get()
while d ==depth:
o.append(node)
node,depth=res.get()
else:
d+=1
output.append(o)
return output
Here is my code for the Breadth First Search (BFS) Iterative Implementation with nodes in each level in the final output is under a single list:
class TreeNode:
def __init__(self, x):
self.val = x
self.left = None
self.right = None
class Solution:
def BFS(self, root) -> int:
level=1
current=(root, level)
s=set()
result=[]
Q = [current]
while Q:
current=Q.pop()
level=current[1]
if current[0] not in s:
result.append([current[0].val, level])
s.add(current[0])
if current[0].left:
Q.insert(0,(current[0].left, level+1))
if current[0].right:
Q.insert(0,(current[0].right, level+1))
output=[]
temp=[]
level=1
for val in result:
if val[1]==level:
temp.append(val[0])
elif val[1] > level:
output.append(temp)
temp=[val[0]]
level+=1
output.append(temp)
return output
Testing:
n1=TreeNode(3)
n2=TreeNode(9)
n3=TreeNode(20)
n4=TreeNode(6)
n5=TreeNode(15)
n6=TreeNode(7)
n1.left=n2
n1.right=n3
n2.left=n4
n3.left=n5
n3.right=n6
sol1=Solution()
print(sol1.BFS(n1))
[[3], [9, 20], [6, 15, 7]]
Thanks for the answer from san. The solution helps me solve problem 107 of leetcode. I revised a little bit based on my understanding. There are two ways to solve this question but I prefer to use BFS fashion. This revised version compresses both value and level in the queue. It provides more flexibility comparing with only print the value of each node in the tree, as other tutorials provided.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def levelOrderBottom(self, root: TreeNode) -> List[List[int]]:
if root is None:
return None
level = 1
q = []
q.append((root,level)) # push both node and level.
save = []
while q:
cur = q.pop(0)
cur_node = cur[0]
cur_level = cur[1]
# print(cur_node.val)
# print(cur_level)
save.append([cur_node.val, cur_level])
if cur_node.left:
q.append((cur_node.left, cur_level+1))
if cur_node.right:
q.append((cur_node.right, cur_level+1))
print(save) # once print, you will have the idea about how to reorgnized the required output.
level = 1
output = []
temp = []
for i in range(len(save)):
cur = save[i]
#print(cur)
if cur[1] == level:
temp.append(cur[0])
if cur[1] != level:
output.insert(0, temp)
temp = []
temp.append(cur[0])
level = level + 1
if i == len(save)-1:
output.insert(0, temp)
return output
You can use the below logic , to print out nodes in BFS .
If you also need , you modify the method to return a list as well .
def levelOrder(root):
qroot = []
print(root.info,end=' ')
if root.left:
qroot.append(root.left)
if root.right:
qroot.append(root.right)
while(qroot):
tmp = qroot[0]
if tmp.left:
qroot.append(tmp.left)
if tmp.right:
qroot.append(tmp.right)
print (tmp.info,end=' ')
qroot.pop(0)
return
Instead of printing the values , you can directly keep appending the value to a new list and return the same .

How to reconstrunct a binary tree from an array produced by preorder tree traversal

I was asked to implement a preorder tree traversal function, that function should have returned an array representing the tree,
and then I was asked to implement a function to reconstruct the tree from the array my previous function returned.
Something like sending a binary tree from one pc and then receiving and reconstructing it on the receiving end.
The important part is that the data should only be transferred once, so I couldn't use the standard preorder and inorder combination.
In my solution, each node is printed, and then added to an array that contains all of the printed nodes, if a node doesn't have a left subtree it will print and add the letter "L", and if the tree doesn't have a right subtree it will print and add the letter "R" to the array.
That part was easy, however, I didn't know how to reconstruct the tree on the receiving side.
Any help or idea will be really appreciated.
Here is what I have done for the sending part:
class TreeNode(object):
"""This class represents a tree."""
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def send(arr_tree, data):
print(data)
arr_tree.append(data)
def send_sub_tree(arr_tree, node):
send(arr_tree, node.data)
if node.left is None:
send(arr_tree, "L")
else:
send_sub_tree(arr_tree, node.left)
if node.right is None:
send(arr_tree, "R")
else:
send_sub_tree(arr_tree, node.right)
if __name__ == '__main__':
tree = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3,
TreeNode(6), TreeNode(7)))
received_tree = []
send_sub_tree(received_tree, tree)
reconstructed_tree = reconstruct_tree(received_tree)
EDIT:
I have managed to implement something that kind-of works, but its messy and doesn't reconstruct the sent part perfectly:
def reconstruct_tree(arr_tree):
node = TreeNode(arr_tree[0])
print(node.data)
if arr_tree[1] == "L" and arr_tree[2] == "R":
if len(arr_tree) > 3 and arr_tree[3] != "L" and arr_tree[3] != "R":
node.right = reconstruct_tree(arr_tree[3:])
else:
return node
if arr_tree[1] != "L":
node.left = reconstruct_tree(arr_tree[1:])
return node
return node
Here is how you could do it. I have also moved your functions inside the class, renamed them, and made some modifications:
class TreeNode(object):
"""This class represents a tree."""
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def to_list(self):
return [self.data] + (
self.left.to_list() if self.left else ["L"]
) + (
self.right.to_list() if self.right else ["R"]
)
#staticmethod
def from_list(lst):
def recurse(it):
try:
data = next(it)
except StopIteration: # Only happens if list is incomplete
return
if data == 'L' or data == 'R':
return
return TreeNode(data, recurse(it), recurse(it))
return recurse(iter(lst))
tree = TreeNode(1,
TreeNode(2,
TreeNode(4),
TreeNode(5)
),
TreeNode(3,
TreeNode(6),
TreeNode(7)
)
)
lst = tree.to_list()
print(lst)
# Reverse operation
recovered_tree = TreeNode.from_list(lst)
# Make that a list again to see if it is the same tree
lst2 = recovered_tree.to_list()
print(lst2) # Same as lst
See it run on repl.it
Note that you could use "L" for the right-side child as well, or "R" for the left one, as the position in the array already leaves no doubt about which child is intended. One special symbol is enough.
Let's think about a general algorithm, using Wikipedia's example of pre-order traversal:
F, B, A, D, C, E, G, I, H.
Let's mark a None for a null subtree:
A = [F, B, A, None, None, D, C, None, None, E, None, None, G, None, I, H, None]
Now we start at the root:
F
-> have a left subtree so insert B
descend
-> have a left subtree so insert A
descend
-> have no left subtree
-> have no right subtree
return
-> have a right subtree so insert D
descend
-> have a left subtree so insert C
descend
-> have no left subtree
-> have no right subtree
return
-> have a right subtree so insert E
descend
-> have no left subtree
-> have no right subtree
return
But how do we know which index and node we return to? One way is to call a recursive function from the node that returns the next index to use (remember here and in the example that follows that i is a local variable):
f(node, i):
# left subtree
if A[i]:
insertLeft(A[i])
i = f(node.left, i + 1)
else:
i = i + 1
#right subtree
if A[i]:
insertRight(A[i])
i = f(node.right, i + 1)
else
i = i + 1
return i
Let's apply to our example:
A = [F, B, A, None, None, D, C, None, None, E, None, None, G, None, I, H, None]
f(F, 1)
insertLeft(B)
i = f(B,2)
insertLeft(A)
i = f(A,3)
i = 4
i = 5
return 5
insertRight(D)
i = f(D,6)
insertLeft(C)
i = f(C,7)
i = 8
i = 9
return 9
insertRight(E)
i = f(C,10)
i = 11
i = 12
return 12
return 12
return 12
insertRight(G) # A[12]
etc...

Can you define multiple different iterators for a Python class?

I'm writing a very simple Tree class:
class Tree:
def __init__(self, value_ = None, children_ = None):
self.value = value_
self.children = children_
I'd like to be able to perform both DFS and BFS traversal with a simple loop, i.e.:
t = Tree()
# ...fill tree...
for node in t:
print(node.value)
In C++, for example, you can have multiple types of iterators - so I could define both a DFS and a BFS iterator and use one or the other depending on what type of traversal I wanted to do. Is this possible to do in Python?
You can have multiple methods returning iterators and have the 'default' one as __iter__. Below is a simple binary tree where 'default' iterator does DFS and which additionally supports BFS with separate method:
from collections import deque
class Tree(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def __iter__(self):
if self.left:
for x in self.left:
yield x
yield self.value
if self.right:
for x in self.right:
yield x
def bfs(self):
q = deque([self])
while q:
x = q.popleft()
if x:
yield x.value
q.extend([x.left, x.right])
Short example of usage:
root = Tree(2)
root.left = Tree(1)
root.right = Tree(4)
root.right.left = Tree(3)
root.right.right = Tree(5)
print list(root) # [1, 2, 3, 4, 5]
print list(root.bfs()) # [2, 1, 4, 3, 5]
You could write separate methods on your class for the two types of iteration. These could for instance be generators that yield values in whatever order you want. You would then write something like:
for node in t.depth_first():
# ...
for node in t.breadth_first():
# ...

Categories