Python Printing Specific Character in Empty Binary Tree Output - python

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.

Related

Inserting a node in a complete binary tree with python

How to insert a node in a complete binary tree without using queue DS? I tried the following code:
class TreeNode:
def __init__(self, value=None) -> None:
self.left = None
self.value = value
self.right = None
class Tree:
def __init__(self, root=None) -> None:
self.__root = root if not root else TreeNode(root)
self.__len = 1 if root else 0
def append(self, data, root="_"):
if self.__root.value is None:
self.__root = TreeNode(data)
self.__len += 1
return
root = self.__root if root == "_" else root
if not root:
return False
if root.left is None and root.right is None:
root.left = TreeNode(data)
self.__len += 1
return True
elif root.left is not None and root.right is None:
root.right = TreeNode(data)
self.__len += 1
return True
elif root.left is not None and root.right is not None:
if self.append(data, root.left):
return
else:
self.append(data, root.right)
return
the recursive call of that function always add the new node on the left side of the tree, so what should I do to make it recursively checks the right side too?
First of all, the first line in your append code seems to give a special meaning to the value in the root node. When it is None it is not considered a real node, but to represent an empty tree. This is not a good approach. An empty tree is represented by a __root that is None -- nothing else. I would also suggest to remove the optional data argument from the constructor. Either the constructor should allow an arbitrary number of values or none at all. To allow one is odd and strengthens the idea that a tree could have a special None in its root node.
To the core of your question. There is nice attribute to complete binary trees. The paths from the root to a leaf can be represented by a bit pattern, where a 0 means "go left" and a 1 means "go right". And the path to the "slot" where a new node should be injected has a relationship with the size of the tree once that node has been added:
new size
binary representation
path to new node
1
1
[]
2
10
[left]
3
11
[right]
4
100
[left,left]
5
101
[left,right]
In general the path to the new node is defined by the bits in the binary representation of the new tree size, ignoring the leftmost 1.
This leads to the following code:
class Tree:
def __init__(self) -> None:
self.__root = None
self.__len = 0
def append(self, data):
self.__len += 1
if self.__len == 1: # First node
self.__root = TreeNode(data)
return
node = self.__root
# Iterate all the bits except the leftmost 1 and the final bit
for bit in bin(self.__len)[3:-1]:
node = [node.left, node.right][int(bit)] # Choose side
if self.__len & 1: # Use final bit to determine where child goes:
node.right = TreeNode(data)
else:
node.left = TreeNode(data)

In binary tree insertion, only the left tree is right. The right tree is wrong

I have a btree class and an insert function, to insert nodes to a tree, breadth wise. But the tree isn't inserting nodes to the right.
I'm creating the root node. The insert function inserts left and right nodes to the root correctly.
Then recursively, I try to insert two nodes to the left node and two to the right node. But in this step, all nodes are added to the left only. Nodes get added to a None parent as well.
I know, I'm making a mistake in the last else statement inside insert function. But I've tried many combinations, but all have resulted in some error.
class BinTree(object):
def __init__(self, val):
self.val = val
self.left = None
self.right = None
def insert(self,val):
if self.left is None:
self.left = BinTree(val)
elif self.right is None:
self.right = BinTree(val)
elif self.left:
self.left.insert(val)
else:
self.right.insert(val)
root = BTree('A')
root.insert('B')
root.insert('C')
root.insert(None)
root.insert('D')
root.insert(None)
root.insert('E')
root.insert('F')
Expected:
A
/ \
B C
/\ /\
None D None E
/
F
Getting:
A
/ \
B C
/ \
None D
/ \
None E
/
F
Your code will traverse to the left as soon as it finds a node there that is not None, which is like a depth-first search (DFS). So the code does not look further at the right side to see if there are still some vacancies to be filled there, but goes left anyway. This results in the bias towards the left of your tree.
Instead, you should use a breadth-first search (BFS) to search for the next vacancy in the tree, so in breadth first order. For that you could use a separate method that will perform this BFS and returns the position of the vacancy (by providing its parent node and which side the new child node should be on).
Here is how that new method would look:
def next_free(self):
queue = [self]
while len(queue):
node = queue.pop(0) # Here you get the nodes in BFS order
if node.val is None: # Cannot have children
continue
for side, child in enumerate((node.left, node.right)):
if child is None: # Found the first vacancy in BFS order!
return node, side
queue.append(child)
Now the insert method becomes trivial:
def insert(self,val):
node, side = self.next_free()
if side == 0:
node.left = Node(val)
else:
node.right = Node(val)
You can see it run on repl.it.
Your tree is build exactly as your code suggest.
The insert function checks if there is an empty child and set if it found one - else it is going recursively to the left (regardless of its length) and that is exactly the tree you get.
Second, your output is not clear - what do you mean by adding None?
In order to achieve building of full tree you need to keep count of your elements.
Then we will be able the use the division on the count by 2 to find the right path (taking leaf or right) till reaching the right leaf. Add self.cnt = 1 to the constructor. Using this for pseudocode for insert:
insert:
cnt = self.cnt++ // Increase the count and get the new value
while (cnt > 0) {
path.push(cnt % 2 == 0 ? left : right) // If even, go left. Else go right.
cnt = cnt / 2
}
path.reverse // We need to start from the last element we pushed
current = head
while (path not empty)
current = current.path.pop
current = val
Try to look at the tree number to understand it better:
1
/ \
2 3
/ \ / \
5 6 7 8
You can't really get the result you want with recursion with the current field you save. Each node only "knows" its current state, and that's why the right side of your tree will forever remain at depth 1.
One solution that comes to mind is adding a right children and left children amount field. This will help tracking the balance. It would look like this:
class Node(object):
def __init__(self, val):
self.val = val
self.left = None
self.right = None
self.right_count = 0
self.left_count = 0
self.even_depth = True
self.needed_to_even = 1
def insert(self, val):
if self.left is None:
self.left = Node(val)
self.left_count += 1
elif self.right is None:
self.right = Node(val)
self.right_count += 1
elif self.left_count > self.right_count + self.needed_to_even or not self.even_depth:
self.even_depth = False
if self.left_count == self.right_count:
self.needed_to_even *= 2
self.even_depth = True
self.right.insert(val)
self.right_count += 1
else:
self.left.insert(val)
self.left_count += 1

Maximum sum in a non-binary tree in Python

I have a non-binary tree and each node of that tree has a value. I would like to get the maximum sum available.
For example:
10
9 8 7
1 2 5 5 15
The return would be 10+7+15=32
I know how to do this if the tree was binary, but what if the tree has n branches?
This code is the binary one taken from the first answer of this question: Find the maximum sum of a tree in python
Assuming each node has a value attribute and a children attribute which is either a list of child nodes, an empty list, or None:
def tree_sum(node):
if node.children:
child_sums = []
for child in node.children:
child_sums.append(tree_sum(child) + node.value)
return max(child_sums)
else:
return node.value
print tree_sum(root)
Here's one approach:
class Node:
def __init__(self, value):
self.value = value
self.children = []
def max_sum(root):
if len(root.children) == 0:
return root.value
sums = []
for child in root.children:
sums.append(root.value + max_sum(child))
return max(sums)
n_9 = Node(9)
n_9.children.extend([Node(1), Node(2), Node(5)])
n_8 = Node(8)
n_8.children.extend([Node(5)])
n_7 = Node(7)
n_7.children.extend([Node(15)])
n_10 = Node(10)
n_10.children = [n_9, n_8, n_7]
print max_sum(n_10)
# Output: 32

Traverse tree and return a node instance after n traversals in python

The end goal is to copy a node from one tree to another tree. I want to visit each node in a binary tree and return a node instance after a number of traverses. I cannot seem to figure out how to return a specific node. Every time the node returned matches the id of the root node since I pass the root node to the function.
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target):
global count
if target == count:
# print 'At target', '.'*25, target
return self
else:
if self.left is not None:
count += 1
self.left.randnode(target)
if self.right is not None:
count += 1
self.right.randnode(target)
If you're doing a DFS and counting iterations, you don't even need recursion, just a stack of places to try, and popping/pushing data.
def dfs(self,target):
count = 0
stack = [start]
while stack:
node = stack.pop()
if count == target:
return node
if node is None: # since we push left/right even if None
continue # stop pushing after we hit None node
stack.extend([self.left,self.right])
return -1 # ran out of nodes before count
Bonus points : swapping stack to a queue for BFS
Apart from that, you might want to pass the count as a parameter, like all self-respecting recursive calls, you can make this stateless ;-)
class node():
def __init__(self):
self.parent = None
self.left = None
self.right = None
def randnode(self, target,count=0):
if target == count:
# print 'At target', '.'*25, target
return self
if self.left is not None:
return self.left.randnode(target,count + 1)
if self.right is not None:
return self.right.randnode(target,count + 1)

Find number of elements smaller than a given element in BST

I have been struggling with this problem for a while and I am a Python beginner when it comes to BST, so I would appreciate some help. I am dynamically adding elements from an (unsorted) array into BST. That part is fine, I know how to do that. The next step, proved to be impossible with my current skill set. As I am adding elements to the tree, I need to be able to find current rank of any element in the tree. I know there are subtleties in this problem, so I would need help to at least find the number of nodes that are below the given node in BST. For example, in this case, node 15 has nodes 10,5 and 13 below it, so the function will return 3. Here is my existing code [this is a problem from Cracking the coding interview, chapter 11]
class Node:
"""docstring for Node"""
def __init__(self, data):
self.data = data
self.left=None
self.right=None
self.numLeftChildren=0
self.numRightChildren=0
class BSTree:
def __init__(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.numLeftChildren+=1
root.left = self.insert(root.left, data)
else:
root.numRightChildren+=1
root.right = self.insert(root.right, data)
return root
def getRankOfNumber(self,root,x):
if root==None:
return 0
else:
if x>root.data :
return self.getRankOfNumber(root.right,x)+root.numLeftChildren+1
elif root.data==x:
return root.numLeftChildren
else:
return self.getRankOfNumber(root.left,x)
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
You can go by this approach:
1. Have 2 more fields in each node numLeftChildren and numRightChildren.
2. Initialize both of them to 0 when you create a new node.
3. At the time of insertion, you make a comparison if the newly added node's
key is less than root's key than you increment, root's numLeftChildren and
call recursion on root's left child with the new node.
4. Do Same thing if new node's key is greater than root's key.
Now, come back to your original problem, You have to find out the number of children in left subtree. Just find out that node in O(logN) time and just print the numLeftChildren
Time Complexity: O(logN)
PS: I have added another field numRightChildren which you can remove if you are always interested in knowing the number of nodes in left subtree only.
You could modify your BST to contain the number of nodes beneath each node.
Or you could iterate over a traditional BST from least to greatest, counting as you go, and stop counting when you find a node of the required value.
You could simplify the code by using just instances of the Node class, as your BSTree instance operates on the root node anyway. Another optimization could be to not represent duplicate values as separate nodes, but instead use a counter of occurrences of the key:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.num_left_children = 0
self.occurrences = 1
def insert(self, data):
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
self.num_left_children += 1
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.occurrences += 1
def get_rank(self, data):
if data < self.data:
return self.left.get_rank(data)
elif data > self.data:
return (self.occurrences + self.num_left_children +
self.right.get_rank(data))
else:
return self.num_left_children
Demo:
root = Node(20)
root.insert(25)
root.insert(15)
root.insert(10)
root.insert(10)
root.insert(5)
root.insert(13)
root.insert(23)
print(root.get_rank(15)) # 4
You can use the below function to find the number of the nodes in the tree (including the root).
def countNodes(self, root):
if root == None:
return 0
else
return (1 + countNodes(root.left) + countNodes(root.right));
To find the number of nodes that lie below root, subtract 1 from the value returned by the function. I think this will help get you started on the problem.
Your code will look like:
class Node:
"""docstring for Node"""
def init(self, data):
self.data = data
self.left=None
self.right=None
self.depth=0
class BSTree:
def init(self):
self.root = None
def addNode(self, data):
return Node(data)
def insert(self, root, data):
if root == None:
return self.addNode(data)
else:
if data <= root.data:
root.left = self.insert(root.left, data)
else:
root.right = self.insert(root.right, data)
return root
BTree=BSTree()
root=BTree.addNode(20)
BTree.insert(root,25)
BTree.insert(root,15)
BTree.insert(root,10)
BTree.insert(root,5)
BTree.insert(root,13)
BTree.insert(root,23)
BTree.insert(root,23)
numLeft = countNodes(root->left);
numRight = countNodes(root->right);
numChildren = numLeft + numRight;

Categories

Resources