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():
# ...
Related
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))
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]
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 .
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).
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