Binary Tree BFS without queue - python

I am working on a solution for https://leetcode.com/problems/binary-tree-level-order-traversal/submissions/.
I want to implement level order traversal without using a queue since that solution is quite trivial.
I have written the following code, which, in theory should work, but is giving the wrong result. I cant work out why.
Any help would be appreciated
class Node:
# A utility function to create a new node
def __init__(self, key):
self.val = key
self.left = None
self.right = None
def height(root):
if root is None:
return 0
else:
heightL = height(root.left)
heightR = height(root.right)
maxHeight = max(heightL, heightR)
return maxHeight + 1
def nodesOnLevel(root, level, result=[]):
if root is None:
return result
if level == 1:
result.append(root.val)
elif level > 1:
nodesOnLevel(root.left, level-1, result)
nodesOnLevel(root.right, level-1, result)
return result
def levelOrder_noQueue(root):
if root is None:
return
levels = height(root)
results = []
for i in range(1, levels+1):
results.append(nodesOnLevel(root, i))
return results;
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
print(levelOrder_noQueue(root))
# output is [[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]
# desired output is [[1], [2,3], [4,5]]

The problem is with result: there is only one result list in your code:
def nodesOnLevel(root, level, result=[]):
The default for result gets evaluated only once, when the script it parsed, not when the function is called. This means that when the levelOrder_noQueue function calls this function a second time (without passing a third argument), result will remain what it was, and the second call will just keep using it to append more elements.
So change this code to something like this:
def nodesOnLevel(root, level, result=None):
if result is None:
result = []
Or, otherwise, don't provide a default, and let the caller provide the empty list:
def nodesOnLevel(root, level, result):
And in levelOrder_noQueue:
results.append(nodesOnLevel(root, i, []))

Related

Want to create a program to scale a linked list by a certain factor

I want to write a function where you input a linked list and a factor, and the function returns a new linked list scaled by that factor. For example:
scale(linkify([1, 2, 3]), 2)
2 -> 4 -> 6 -> None
First, I made a function that, when you input a list of items, converts into a linked list. This is it here:
def linkify(item: list[int]) -> Optional[Node]:
"""Return a Linked List of Nodes with same values and same order as input list."""
if len(item) == 0:
return None
elif len(item) == 1:
return Node(item[0], None)
else:
return Node(item[0], linkify(item[1:]))
Now, I'm trying to write the function in order to scale that list.
Here is what I have now for the function:
def scale(head: Optional[Node], factor: int) -> Optional[Node]:
"""Returns new linked list of nodes where each value in original list is scaled by scaling factor."""
if head is None:
return None
else:
return Node(head.data * factor, scale(head.next, factor))
However, when I try to test this, I get an error saying exercises.ex11.linked_list.Node object at 0x0000013392C97C10>, and I'm not entirely sure what this means. Can anyone tell me what I'm getting wrong?
Also, I have to create the function recursively, and can't use any other functions outside the ones I've created.
Here is the test case I created as well:
def test_scale_factor() -> None:
linked_list: list[int] = [1, 2, 3]
linked_list_2: list[int] = [2, 4, 6]
assert is_equal(scale(linkify(linked_list), 2), linkify(linked_list_2))
Thanks for your help!
Is this a proper solution for your case?
I always try to avoid using recursions.
Sure you also can add some checks on the function inputs.
class Node:
def __init__(self, data = None):
self.data = data
self.next = None
class Linkedlist:
def __init__(self):
self.head = None
def linkify(self, nodes):
if len(nodes) == 0:
return
self.head = node = Node()
for i in nodes:
node.next = Node(i)
node = node.next
self.head= self.head.next
def is_equal(self, other_llist):
node1 = self.head
node2 = other_llist.head
while node1 and node2:
if node1.data != node2.data:
return False
node1 = node1.next
node2 = node2.next
if node1 or node2:
return False
return True
def scale(self, factor):
node = self.head
while node:
node.data *= factor
node = node.next
llist1 = Linkedlist()
llist1.linkify([1, 2, 3])
llist1.scale(2)
llist2 = Linkedlist()
llist2.linkify([2, 4, 6])
print(llist1.is_equal(llist2))

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]

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 .

Zigzag level order traversal

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).

reverse linked lists recursively

I have a problem with an algorithm which reverses Linked Lists recursively in Python.
def reverse(lis, prev = None):
if lis.next != None:
reverse(lis.next, lis)
lis.next = prev
Input: 3 1 4
Output: 3
Any idea why its not working?
The problem is that the root of your linked list has changed as well: after altering the list, your element 3 is indeed the last element, your function better returns the root as well:
def reverse(lis, prev = None):
if lis.next != None:
temp = lis.next
lis.next = prev
return reverse(temp, lis)
else:
return lis # the new root
so now you call it with:
oldroot = ... #construct linked list
newroot = reverse(oldroot)
print(newroot)
So your function is correct, but you have the wrong linked list element in your hands after the operation.
This looks like some functional programming exercise, so I present you a functional solution (it copies the list):
def reverse(link, rest=None):
if link is None:
return rest
return reverse(link.next, LinkedList(link.value, rest))
class LinkedList:
def __init__(self, value=None, next=None):
self.next = next
self.value = value
def print(self):
print(self.value)
if self.next is not None:
self.next.print()
def to_linked_list(items):
if len(items) == 0:
return None
return LinkedList(items[0], to_linked_list(items[1:]))
to_linked_list([1, 2, 3, 4, 5]).print()
# 1, 2, 3, 4, 5
reverse(to_linked_list([1, 2, 3, 4, 5])).print()
# 5, 4, 3, 2, 1
In place instead of creating a new chain:
def reverse_linked_list(node, prev=None):
if node.next is None:
node.next = prev
return node
root_node = reverse_linked_list(node.next, node)
node.next = prev
return root_node

Categories