Print Levels Of A Binary Tree by Level Iteratively - python

I want to print a binary tree level by level iteratively without using a dequeue or any other data structure besides a python list. I have looked online and most do it one of those ways mentioned above.
If I have a tree:
41
/ \
7 53
/ \ /
1 19 47
I want it to print like:
41
7
53
1
19
47
This is my shot at it but it doesn't print out all the values in the bst:
def levelorder(self):
current = self._root
current_left = current_right = self._root
a = [current._value]
while current_left is not None and current_right is not None:
current_left = current_left._left
current_right = current_right._right
if current_left is not None:
a.append(current_left._value)
if current_right is not None:
a.append(current_right._value)
return a
This is what it outputs:
[41, 7, 53, 1]
Any ideas what is wrong with my code? and how I can approach the solution to this problem?
TREE CLASS:
class _BSTNode:
def __init__(self, value):
self._value = copy.deepcopy(value)
self._left = None
self._right = None
self._height = 1
return
class BST:
def __init__(self):
self._root = None
self._count = 0
self.comparisons = 0
return
def levelorder(self):
levels = [[self._root]]
while levels[-1]:
nextlevel = []
for node in levels[-1]:
nextlevel.extend([node for node in (node._left, node._right) if node])
levels.append(nextlevel)
return levels[:-1]
and my main part:
b = BST()
b.insert(41)
b.insert(7)
b.insert(53)
b.insert(1)
b.insert(19)
b.insert(47)
print (b.levelorder())

Without testing, as I don't have your Tree class, but I hope the general idea applies:
def levelorder(tree):
levels = [ [tree.root] ]
while levels [-1]:
nextLevel = []
for node in levels [-1]:
nextLevel.extend ( [node for node in (node.left, node.right) if node] )
levels.append (nextLevel)
return levels # or maybe levels [:-1]
Fully working example:
#! /usr/bin/python3
class Node:
def __init__(self, payload, left = None, right = None):
self.payload = payload
self.left = left
self.right = right
def __repr__(self):
return self.payload
class Tree:
def __init__(self, root):
self.root = root
def levels(self):
levels = [[self.root]]
while levels[-1]:
nextLevel = []
for node in levels[-1]:
nextLevel.extend([node for node in (node.left, node.right) if node])
levels.append(nextLevel)
return levels[:-1]
t = Tree(Node('41', Node('7', Node('1'), Node('19')), Node('53', Node('47'))))
print(t.levels())
Output is [[41], [7, 53], [1, 19, 47]]

Related

Python binary tree use build instead of Node

class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def printPaths(root):
path = []
printPathsRec(root, path, 0)
def printPathsRec(root, path, pathLen):
if root is None:
return
if(len(path) > pathLen):
path[pathLen] = root.data
else:
path.append(root.data)
pathLen = pathLen + 1
if root.left is None and root.right is None:
printArray(path, pathLen)
else:
printPathsRec(root.left, path, pathLen)
printPathsRec(root.right, path, pathLen)
def printArray(ints, len):
for i in ints[0 : len]:
print(i," ",end="")
print()
from binarytree import build
values = [7, 3, 2, 6, 9, None, 1, 5, 8]
root = build(values)
print(root)
printPaths(root.value)
.
I need to build binary tree with build and make the code work, but i can't find out the way.
This example is get from internet,they use method like
root = Node(10)
root.left = Node(8)
root.right = Node(2)
root.left.left = Node(3)
root.left.right = Node(5)
root.right.left = Node(2)
printPaths(root)
But i need to use another method to make it happen.
I am going to guess that values contains the level-order values of the tree, where the children below a None are not included (instead of having two filler None values).
Then the algorithm to populate a new tree will also follow a level-order way of populating. For this you typically use a queue or deque or two lists. For example:
def build(values):
if not values:
return
it = iter(values)
root = Node(next(it))
level = [root]
while level:
nextlevel = []
for parent in level:
value = next(it, None)
if value is not None:
parent.left = Node(value)
nextlevel.append(parent.left)
value = next(it, None)
if value is not None:
parent.right = Node(value)
nextlevel.append(parent.right)
level = nextlevel
return root
To create binary Tree:
you need a list sequential.
class Node:
count = 0
def __init__(self, val):
self.left = None
self.right = None
self.val = val
Node.count+= 1
def __str__(self):
return (f"value is {self.val} \ncount is:{self.count}")
If info is given as:
from collections import deque
d = {0:[1,2], 2:[4,5], 1:[3]} # how nodes are connected. key is node number and values is node num of children.
vals = [10,11,12,13,14,15] # values associated with the node num
q = deque()
q.append(0) #<---- root node
li = [vals[0]] #<--- root node val
while(len(q)>0):
front = q.popleft()
if (d.get(front, 0)):
if(len(d[front])==2):
li.append(vals[d[front][0]])
li.append(vals[d[front][1]])
q.append(d[front][0])
q.append(d[front][1])
else:
li.append(vals[d[front][0]])
li.append(None)
q.append(d[front][0])
# q.append(d[front][1])
else:
li.append(None)
li.append(None)
This will give you list
li:
[10, 11, 12, 13, None, 14, 15, None, None, None, None, None, None]
build:
def build(values):
if not values:
return None
it = iter(values)
root=Node(next(it))
q = deque()
q.append(root)
while(q):
front = q.popleft()
val1 = next(it, None)
if (val1):
n1 = Node(val1)
q.append(n1)
front.left = n1
val2 = next(it, None)
if (val2):
n2 = Node(val2)
q.append(n2)
front.right = n2
return root
node = build(li)
To print in inorder:
def print_node(root):
if not root:
return
print_node(root.left)
print(root.val, end=" ")
print_node(root.right)
print_node(node)
13 11 10 14 12 15
Straight Forward:
from collections import deque
d = {0:[1,2], 2:[4,5], 1:[3]}
vals = [10,11,12,13,14,15]
q = deque()
root = Node(vals[0], 0)
q.append(root)
while(q):
node = q.popleft()
if (d.get(node.node_num, {})):
if(len(d[node.node_num])==2):
no1 = d[node.node_num][0]
node.left = Node(vals[no1],no1)
no2 = d[node.node_num][1]
node.right = Node(vals[no2], no2)
q.append(node.left)
q.append(node.right)
else:
no1 = d[node.node_num][0]
node.left = Node(vals[no1],no1)
q.append(node.left)
print_node(root)
13 11 10 14 12 15
Added 1 attribute for node_num in class Node

Binary Tree Iterative Inorder Traversal

I'm trying to implement an iterative inorder traversal of a binary tree.
node.py:
class Node:
def __init__(self, node=None, left=None, right=None):
self.node = node
self.left = left
self.right = right
inorder_traversal.py:
from node import Node
def in_order(root):
stack = nodes = []
while stack or root:
if root:
stack.append(root)
root = root.left
else:
current = stack.pop()
nodes.append(current.node)
root = current.right
return nodes
def main():
'''
Construct the below binary tree:
15
/ \
/ \
/ \
10 20
/ \ / \
8 12 16 25
'''
root = Node(15)
root.left = Node(10)
root.right = Node(20)
root.left.left = Node(8)
root.left.right = Node(12)
root.right.left = Node(16)
root.right.right = Node(25)
print(in_order(root))
if __name__ == '__main__':
main()
I've been getting: AttributeError: 'int' object has no attribute 'node'.
How can I resolve this error?
stack = nodes = [] creates two references to the same list object.
When you do stack.append(root) or nodes.append(current.node) this affects both stack and nodes because they are the same. What you want is 2 different objects:
stack = []
nodes = []
Then you'll get this output: [8, 10, 12, 15, 16, 20, 25]
The value of the node variable is initialized to an Int in your code (e.g. Node(5)) and your in_order method push that value on the stack and later pop it and try to access its node variable, which will result in the error.
Here's an implementation that does not have that error and uses recursion for the in order traversal (which can be simpler to follow).
class Node:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def in_order(node):
nodes = []
if node.left:
nodes.extend(in_order(node.left))
nodes.append(node.value)
if node.right:
nodes.extend(in_order(node.right))
return nodes

How can I find the depth of a specific node inside a binary tree?

I'm trying to figure out a recursive solution to this problem. The main thing is to return the level in the binary tree where the node is.
def find_depth(tree, node):
if node == None:
return 0
else:
return max(find_depth(tree.left))
#recursive solution here
Using this class for the values:
class Tree:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
Example: Calling find_depth(tree, 7) should return the level where 7 is in the tree. (level 2)
3
/ \
7 1 <------ return that 7 is at level 2
/ \
9 3
maybe this is what you are looking for
def find_depth(tree, node):
if node is None or tree is None:
return 0
if tree == node:
return 1
left = find_depth(tree.left, node)
if left != 0:
return 1 + left
right = find_depth(tree.right, node)
if right != 0:
return 1 + right
return 0
You need to provide information about depth in find_depth call. It might look like this (assuming 0 is a sentinel informing that node is not found):
def find_depth(tree, node, depth=1):
if node == None:
return 0
if tree.value == node:
return depth
left_depth = find_depth(tree.left, node, depth+1)
right_depth = find_depth(tree.right, node, depth+1)
return max(left_depth, right_depth)
Then you call it with two parameters: x = find_depth(tree, 7).
Recursion is a functional heritage and so using it with functional style yields the best results -
base case: if the input tree is empty, we cannot search, return None result
inductive, the input tree is not empty. if tree.data matches the search value, return the current depth, d
inductive, the input tree is not empty and tree.data does not match. return the recursive result of tree.left or the recursive result of find.right
def find (t = None, value = None, d = 1):
if not t:
return None # 1
elif t.value == value:
return d # 2
else:
return find(t.left, value, d + 1) or find(t.right, value, d + 1) # 3
class tree:
def __init__(self, value, left = None, right = None):
self.value = value
self.left = left
self.right = right
t = tree \
( 3
, tree(7, tree(9), tree(3))
, tree(1)
)
print(find(t, 7)) # 2
print(find(t, 99)) # None
You can implement the find method in your tree class too
def find (t = None, value = None, d = 1):
# ...
class tree
def __init__ #...
def find(self, value)
return find(self, value)
print(t.find(7)) # 2
print(t.find(99)) # None

Level-filled binary tree

To better understand recursive functions, I'm trying to create a Python script for a binary tree that inserts values, filling levels entirely before going to the next one.
This is my tree-node implementation:
class Node:
def __init__(self, value):
self.value = value
self.right_child = None
self.left_child = None
self.parent = None
class Tree:
def __init__(self):
self.root = None
What I'm having problems with right now is setting the conditions for that criteria to be met.
An example: here i'm adding, in order, the following numbers: 12, 5, 18, 2, 9 , 15, 19, 13, 17. Thus, the only condition for putting stuff in the next level is that the parent one is filled.
_12_______
/ \
5 __18_
/ \ / \
2 9 15_ 19
/ \
13 17
This is what I have so far:
def insert(self,value):
if(self.root==None):
self.root=Node(value)
else:
self._insert(value,self.root)
def _insert(self, value, curNode):
if(curNode.left_child==None):
curNode.left_child=Node(value)
curNode.left_child.parent = curNode
elif(curNode.right_child==None):
curNode.right_child=Node(value)
curNode.right_child.parent = curNode
else:
self._insert(value,curNode.left_child)
which gives:
_12_
/ \
__5 18
/ \
__2_ 9
/ \
15_ 19
/ \
13 17
Thus ignoring all right children. The problem is, of course, the last else of my code. How can I make this consider both the left and right children of the node?
You actually don't need a node structure with left and right pointers for this. Just store the whole tree in an array so that the children of the node at the index N are at 2*N+1 and 2*N+2:
def print_tree(items, pos, level):
if pos >= len(items):
return
print('.' * level, items[pos])
print_tree(items, pos * 2 + 1, level + 1)
print_tree(items, pos * 2 + 2, level + 1)
print_tree([12, 5, 18, 2, 9 , 15, 19, 13, 17], 0, 0)
prints
12
. 5
.. 2
... 13
... 17
.. 9
. 18
.. 15
.. 19
which is what you want.
This is called a binary heap.
If you're looking for a search tree (one that maintains the order of values), and want to keep it balanced, have a look at https://en.wikipedia.org/wiki/Self-balancing_binary_search_tree
georg's answer is the smart way to represent this. However, if you are interested in how you would operate with the tree structure to get the same result, you can split the problem in two parts: first find the shallowest node which is not complete, then add a new node to it. This is one way to do that:
class Node:
def __init__(self, value):
self.value = value
self.right_child = None
self.left_child = None
self.parent = None
class Tree:
def __init__(self):
self.root = None
def insert(self, value):
if self.root is None:
self.root = Node(value)
else:
# Find shallowest incomplete node (ignore returned depth)
node, _ = Tree._next_insertion_node(self.root)
# Add new node to it
if node.left_child is None:
node.left_child = Node(value)
else:
node.right_child = Node(value)
#staticmethod
def _next_insertion_node(node, level=0):
# Returns shallowest incomplete node and its depth
if node.left_child is None or node.right_child is None:
return node, level
node_left, level_left = Tree._next_insertion_node(node.left_child, level + 1)
node_right, level_right = Tree._next_insertion_node(node.right_child, level + 1)
if level_left <= level_right:
return node_left, level_left
else:
return node_right, level_right
def print(self):
Tree._print_node(self.root)
#staticmethod
def _print_node(node, level=0):
if node is None: return
print(' ' * (level * 2), '*', node.value)
Tree._print_node(node.left_child, level + 1)
Tree._print_node(node.right_child, level + 1)
numbers = [12, 5, 18, 2, 9 , 15, 19, 13, 17]
tree = Tree()
for number in numbers:
tree.insert(number)
tree.print()
# * 12
# * 5
# * 2
# * 13
# * 17
# * 9
# * 18
# * 15
# * 19
Not sure this is the best way of doing it but I tried to write something similar to the approach you tried. Instead of immediately going to the left node, we record both nodes, and then iterate through in order that we've seen them. So first the root, then root.left, root.right, root.left.left, root.left.right, root.right.left ... etc
class Node:
def __init__(self, value):
self.value = value
self.right_child = None
self.left_child = None
self.parent = None
class Tree:
def __init__(self):
self.root = None
# record nodes that need processing.
nodes_to_process = []
def insert(self,value):
if(self.root==None):
self.root=Node(value)
else:
# add the node to be processed
self.nodes_to_process.append(self.root)
self._insert(value)
def _insert(self,value):
# the current node is the next node in the list.
curNode = self.nodes_to_process[0]
if(curNode.left_child==None):
curNode.left_child=Node(value)
curNode.left_child.parent = curNode
# reset the list, since we've inserted.
self.nodes_to_process = []
elif(curNode.right_child==None):
curNode.right_child=Node(value)
curNode.right_child.parent = curNode
# reset the list, since we've inserted.
self.nodes_to_process = []
else:
# insert the two child nodes.
self.nodes_to_process.append(curNode.left_child)
self.nodes_to_process.append(curNode.right_child)
# Remove the node we've just examined.
self.nodes_to_process.pop(0)
self._insert(value)
Here's a quick test.
tree = Tree()
for number in [12, 5, 18, 2, 9 , 15, 19, 13, 17]:
tree.insert(number)
print(tree.root.value) # 12
print(tree.root.left_child.value) #5
print(tree.root.left_child.left_child.value) # 2
print(tree.root.left_child.left_child.left_child.value) # 13
print(tree.root.left_child.left_child.right_child.value) # 17

Creating a binary tree

I'm trying to create a tree from a flat list. I need to define a function called tree_from_flat_list. For any node at index position i, the left child is stored at index position 2*i, and the right child is stored at index position 2*i+1. :
class BinaryTree:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def get_left(self):
return self.left
def get_right(self):
return self.right
def set_left(self, tree):
self.left = tree
def set_right(self, tree):
self.right = tree
def set_data(self, data):
self.data = data
def get_data(self):
return self.data
def create_string(self, spaces):
info = ' ' * spaces + str(self.data)
if self.left != None:
info += '\n(l)' + self.left.create_string(spaces+4)
if not self.right == None:
info += '\n(r)' + self.right.create_string(spaces+4)
return info
def __str__(self):
representation = self.create_string(0)
return representation
def tree_from_flat_list(node_list):
if node_list != None:
root_index = 1
list1 = []
list2 = []
root = node_list[root_index]
left_sub_tree = list1.append(node_list[2*root_index])
right_sub_tree = list2.append(node_list[2*root_index+1])
tree = BinaryTree(root)
tree.set_left(tree_from_flat_list(left_sub_tree))
tree.set_right(tree_from_flat_list(right_sub_tree))
return tree
When I try running this:
def test():
flat_list = [None, 10, 5, 15, None, None, 11, 22]
my_tree = tree_from_flat_list(flat_list)
print(my_tree)
test()
I should get the output:
10
(l) 5
(r) 15
(l) 11
(r) 22
Edit: Still stuck on what I should be doing for the function. Any help is still appreciated.
the amount of spaces inbetween is the height of the tree and the l and r represent if they are a left child or a right child. This would looks like:
10
/ \
5 15
/ \
11 22
but instead I only get:
10
How should I edit my tree_from_flat_list function so that this works. Any help is appreciated. Thank you.
The essence of your problem is in these lines:
left_sub_tree = list1.append(node_list[2*root_index])
right_sub_tree = list2.append(node_list[2*root_index+1])
The append function sets doesn't return anything - it appends to the list. This sets your left and right sub trees to None.
The list format seems to be a variant of a binary heap where elements can be None. I think you can simplify this quite a bit:
class BinaryTree(object):
def __init__(self, label, left, right):
self.label = label
self.left = left
self.right = right
def tree_from_flat_list(ls, index=1):
if index < len(ls) and ls[index] is not None:
left = tree_from_flat_list(ls, 2*index)
right = tree_from_flat_list(ls, 2*index+1)
return BinaryTree(ls[index], left, right)
I wonder though why you don't store the left and right children in indices 2*i+1 and 2*i+2, like in a binary heap; then you don't need to have the None at the beginning.

Categories

Resources