Level-filled binary tree - python

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

Related

Python Printing Specific Character in Empty Binary Tree Output

I have a binary tree generated as follows
class Node:
def __init__(self,data):
# Left Child
self.left = None
# Right Child
self.right = None
# Node Value
self.data = data
def insert(self, data):
# Compare the new value with the parent node
if self.data:
# Less than or equal goes to the Left
if data <= self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
# Greater than goes to the right
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.data = data
Now suppose we populate the binary tree with the following
Numbers = [4, 7, 9, 1 , 3]
for index in range(len(Numbers)):
if index == 0:
root = Node(Numbers[index])
else:
root.insert(Numbers[index])
Which generates the following binary tree:
4
/ \
1 7
/ \ / \
. 3 . 9
I have written the following to print the binary tree:
def PrintTree(self, root):
thislevel = [root]
while thislevel:
nextlevel = list()
Curr_Level = ""
for n in thislevel:
Curr_Level += " " + str(n.data)
if n.left:
nextlevel.append(n.left)
if n.right:
nextlevel.append(n.right)
print(Curr_Level)
thislevel = nextlevel
root.PrintTree(root=root)
which generates the following output
4
1 7
3 9
However, I would like the code print the empty entries with an "#", i.e. I want my code to output the following:
4
1 7
# 3 # 9
How can I go about adjusting my PrintTree function to achieve this?
Run a modified version of BFS. More specifically, run breadth first search (BFS) starting from the root node. This will assign a level to each node. During BFS, when a current node has a missing child, add an # node in its place to the FIFO queue. Print a node each time it is removed from the queue. Print in a new line each time a node with a new level is removed from the queue.

Sum levels wise binary tree

I must implement a sum for each level in a binary tree
I must return a list that contains at position i, the ith sum
example: if I have this tree
24
/ \
14 27
/ \ / \
11 20 12 55
I must return [24, 41, 98]
I tried to implement this solution in python
def sommaperlivelli(p, lista):
if p == None:
return
if p.left != None and p.right != None:
lista.append(p.left.key + p.right.key)
sommaperlivelli(p.left, lista)
sommaperlivelli(p.right, lista)
return lista
I can get only the first sum and I can't add the root. How I can do it?
this is the class that I use
class NodoABR:
def __init__(self, key = None, left = None, right = None, parent = None):
self.key = key
self.left = left
self.right = right
self.parent = parent
this is how I add node to the tree
def inserisciNodo(p, n):
if p == None:
return NodoABR(n)
else:
if p.key == n:
return p
elif p.key < n:
rchild = inserisciNodo(p.right, n)
p.right = rchild
rchild.parent = p
else:
lchild = inserisciNodo(p.left, n)
p.left = lchild
lchild.parent = p
return p
this is a BinarySearchTree
in main function I do this
p = NodoABR(24)
p = inserisciNodo(p, 14)
p = inserisciNodo(p, 27)
p = inserisciNodo(p, 11)
p = inserisciNodo(p, 20)
p = inserisciNodo(p, 12)
p = inserisciNodo(p, 55)
print(sommaperlivelli(p,[]))
Assuming you have similar Tree Node class, you can try this & modify to suite your particular needs.
from collections import deque
class Node: # TreeNode eg.
def __init__(self, key):
self.data = key
self.left = None
self.right = None
def LevelSum(root):
answer = []
if (root == None): return 0 # base case
result = root.data
# track of # nodes at every level when traversal, and sum them up
q = deque()
q.append(root)
while len(q): # > 0):
size = len(q)
# Iterate for all the nodes
tot = 0
while size: # meaning --- size > 0
# Dequeue an node from queue
tmp = q.popleft()
# Add this node's value to current sum.
tot += tmp.data
# Enqueue left and right children of dequeued node
if (tmp.left != None): q.append(tmp.left)
if (tmp.right != None): q.append(tmp.right)
size -= 1
print(tot, result)
answer.append(tot)
return answer
Example:
if __name__ == '__main__':
root = Node(1)
root.left = Node(2)
root.right = Node(3)
root.left.left = Node(4)
root.left.right = Node(5)
root.right.right = Node(6)
root.right.right.left = Node(9)
root.right.right.right = Node(11)
"""
# Constructed Binary tree is:
# 1
# / \
# 2 3 # 5
# / \ \
# 4 5 6 # 15
# / \
# 9 11 # 20
"""
print("level sum is", LevelSum(root))
[Edit] The PO author wants to learn another approach. (non-queue)
from collections import Counter
from typing import List
class Node:
def __init__(self, key):
self.data = key
self.left = None
self.right = None
def level_sum(root: Node) -> int:
def dfs(node, level):
if not node: return
total[level] += node.data
dfs(node.left,level + 1)
dfs(node.right,level + 1)
total = Counter()
dfs(root, 1)
print(total) # showing each level & its total --- can be commented out
return total.most_common()[0][1] # the largest total
Using your NodoABR class, here's a simple tree traversal, depth-first, where we note the level at each iteration, and use it to store the node values, arr is an array (or list) of lists, one for each level:
# Create sample tree
root = NodoABR(24, NodoABR(14, NodoABR(11), NodoABR(20)), NodoABR(27, NodoABR(12), NodoABR(55)))
def handle_node(nd, level):
# Add a new level list if needed
if len(arr) == level:
arr.append([])
# Append this node value to its proper level list
arr[level].append(nd.key)
# Recurse, increasing the level
if nd.left is not None:
handle_node(nd.left, level+1)
if nd.right is not None:
handle_node(nd.right, level+1)
# Main program
arr = []
handle_node(root, 0)
print(arr)
And this is the output:
[[24], [14, 27], [11, 20, 12, 55]]

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

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.

Print Levels Of A Binary Tree by Level Iteratively

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]]

Categories

Resources