Inserting value into Binary Tree in Python - python

I need to add values to my Binary Tree (Node).
Here is my class:
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def __str__(self):
return "{}".format(self.val)
file = open("/home/dzierzen/Downloads/text.txt", "r")
lines = []
for line in file:
cleaned_line = re.sub(r"\s+", "", line)
#
I have txt file with something like this. L means Left, Right mean Right on the tree. Of course the real txt file contains much more records. My questions is: how to deal with that? How to add this values to the tree?
G RR
A
C L
F LLR
X LLL
F R
X RL
H LLG RR
C L
F LLR
X LLL
F R
X RL
H LL

I'm building the tree by going through the list and deciding on the L/R characters which way to go. When I found a non existing leaf, I create a Node there with the value of None. Multiple visits to the same leaf will overwrite the value, but you can easily change that. If the splitted text has only one value, that will be the root Node's value.
I added a printing method that will traverse the tree and print the root of the subtree, then the left and right leaves. It also indents it according to it's level.
class Node:
def __init__(self, key):
self.left = None
self.right = None
self.val = key
def __str__(self):
return "{}".format(self.val)
def _printTree(self, node, level=0):
if node != None:
print(f"{'-'*level}{node}")
self._printTree(node.left,level+1)
self._printTree(node.right,level+1)
def printTree(self):
self._printTree(self)
p = s.split('\n')
root = Node(None)
for k in p:
v = k.split()
if len(v) == 1:
root.val = v[0]
else:
r = root
for c in v[1]:
if c == 'L':
if r.left is None:
r.left = Node(None)
r = r.left
elif c == 'R':
if r.right is None:
r.right = Node(None)
r = r.right
r.val = v[0]
root.printTree()
Using the input text you wrote this is what gets generated:
A
-C
--H
---X
---F
-F
--X
--G

Related

Python recursive path finder for binary tree

I'm trying to write a recursive function in python that given a binary tree and a node returns a string containing directions to the node. I've got close but my final return statement gives me the path plus the node (I don't need the node) i.e LRLR4.
here is my code so far:
class Tree:
def __init__(self):
self.root = None
self.left = None
self.right = None
def join(item: object, left: Tree, right: Tree):
tree = Tree()
tree.root = item
tree.left = left
tree.right = right
return tree
def path(tree: Tree, node: str, out: str=""):
if not tree:
return ""
if tree.root == node:
return tree.root
res = path(tree.left, node)
if res:
return "L" + res
res = path(tree.right, node)
if res:
return "R" + res
Is there a way I can implement this without the node on the end of the string output?
Edit: added all actual code and the tree in question contains single letter strings for each node.
To write path -
def path(tree, target):
if not tree:
return ""
elif target < tree.root:
return "L" + path(tree.left, target)
elif target > tree.root:
return "R" + path(tree.right, target)
else:
return "" # tree.root equal to target; don't return node
We can make some more improvements to your Tree class though. See these assignments in join?
def join(item: object, left: Tree, right: Tree):
tree = Tree()
tree.root = item
tree.left = left
tree.right = right
return tree
It would be better if the Tree constructor takes these values as arguments -
class Tree:
def __init__(self, root, left = None, right = None):
self.root = root
self.left = left
self.right = right
def join(item, left, right):
return Tree(item, left, right) # pass as arguments
Now the join function is redundant and can be removed -
class Tree:
def __init__(self, root, left = None, right = None):
self.root = root
self.left = left
self.right = right
# no more need for `join`
Given mytree -
# g
# / \
# / \
# d m
# / \ / \
# b f j q
# / \
# a k
mytree = \
Tree("g",
Tree("d", Tree("b", Tree("a")), Tree("f")),
Tree("m", Tree("j", None, Tree("k")), Tree("q"))
)
print(path(mytree, "f")) # LR
print(path(mytree, "k")) # RLR
print(path(mytree, "q")) # RRR

How to reconstrunct a binary tree from an array produced by preorder tree traversal

I was asked to implement a preorder tree traversal function, that function should have returned an array representing the tree,
and then I was asked to implement a function to reconstruct the tree from the array my previous function returned.
Something like sending a binary tree from one pc and then receiving and reconstructing it on the receiving end.
The important part is that the data should only be transferred once, so I couldn't use the standard preorder and inorder combination.
In my solution, each node is printed, and then added to an array that contains all of the printed nodes, if a node doesn't have a left subtree it will print and add the letter "L", and if the tree doesn't have a right subtree it will print and add the letter "R" to the array.
That part was easy, however, I didn't know how to reconstruct the tree on the receiving side.
Any help or idea will be really appreciated.
Here is what I have done for the sending part:
class TreeNode(object):
"""This class represents a tree."""
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def send(arr_tree, data):
print(data)
arr_tree.append(data)
def send_sub_tree(arr_tree, node):
send(arr_tree, node.data)
if node.left is None:
send(arr_tree, "L")
else:
send_sub_tree(arr_tree, node.left)
if node.right is None:
send(arr_tree, "R")
else:
send_sub_tree(arr_tree, node.right)
if __name__ == '__main__':
tree = TreeNode(1, TreeNode(2, TreeNode(4), TreeNode(5)), TreeNode(3,
TreeNode(6), TreeNode(7)))
received_tree = []
send_sub_tree(received_tree, tree)
reconstructed_tree = reconstruct_tree(received_tree)
EDIT:
I have managed to implement something that kind-of works, but its messy and doesn't reconstruct the sent part perfectly:
def reconstruct_tree(arr_tree):
node = TreeNode(arr_tree[0])
print(node.data)
if arr_tree[1] == "L" and arr_tree[2] == "R":
if len(arr_tree) > 3 and arr_tree[3] != "L" and arr_tree[3] != "R":
node.right = reconstruct_tree(arr_tree[3:])
else:
return node
if arr_tree[1] != "L":
node.left = reconstruct_tree(arr_tree[1:])
return node
return node
Here is how you could do it. I have also moved your functions inside the class, renamed them, and made some modifications:
class TreeNode(object):
"""This class represents a tree."""
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def to_list(self):
return [self.data] + (
self.left.to_list() if self.left else ["L"]
) + (
self.right.to_list() if self.right else ["R"]
)
#staticmethod
def from_list(lst):
def recurse(it):
try:
data = next(it)
except StopIteration: # Only happens if list is incomplete
return
if data == 'L' or data == 'R':
return
return TreeNode(data, recurse(it), recurse(it))
return recurse(iter(lst))
tree = TreeNode(1,
TreeNode(2,
TreeNode(4),
TreeNode(5)
),
TreeNode(3,
TreeNode(6),
TreeNode(7)
)
)
lst = tree.to_list()
print(lst)
# Reverse operation
recovered_tree = TreeNode.from_list(lst)
# Make that a list again to see if it is the same tree
lst2 = recovered_tree.to_list()
print(lst2) # Same as lst
See it run on repl.it
Note that you could use "L" for the right-side child as well, or "R" for the left one, as the position in the array already leaves no doubt about which child is intended. One special symbol is enough.
Let's think about a general algorithm, using Wikipedia's example of pre-order traversal:
F, B, A, D, C, E, G, I, H.
Let's mark a None for a null subtree:
A = [F, B, A, None, None, D, C, None, None, E, None, None, G, None, I, H, None]
Now we start at the root:
F
-> have a left subtree so insert B
descend
-> have a left subtree so insert A
descend
-> have no left subtree
-> have no right subtree
return
-> have a right subtree so insert D
descend
-> have a left subtree so insert C
descend
-> have no left subtree
-> have no right subtree
return
-> have a right subtree so insert E
descend
-> have no left subtree
-> have no right subtree
return
But how do we know which index and node we return to? One way is to call a recursive function from the node that returns the next index to use (remember here and in the example that follows that i is a local variable):
f(node, i):
# left subtree
if A[i]:
insertLeft(A[i])
i = f(node.left, i + 1)
else:
i = i + 1
#right subtree
if A[i]:
insertRight(A[i])
i = f(node.right, i + 1)
else
i = i + 1
return i
Let's apply to our example:
A = [F, B, A, None, None, D, C, None, None, E, None, None, G, None, I, H, None]
f(F, 1)
insertLeft(B)
i = f(B,2)
insertLeft(A)
i = f(A,3)
i = 4
i = 5
return 5
insertRight(D)
i = f(D,6)
insertLeft(C)
i = f(C,7)
i = 8
i = 9
return 9
insertRight(E)
i = f(C,10)
i = 11
i = 12
return 12
return 12
return 12
insertRight(G) # A[12]
etc...

Python Trees: Modifying a tree

This is my python code to make an Ordered Binary Decision Diagram (not very relevant for the context). So I just have a tree of a particular height, and I need to set some of the leaf nodes to one. So I have a variable path which involves an array of "decisions", to go left or right from that particular node. But my code is by mistake modifying multiple roots. I am fairly new to Python and I used to rely on pointers when I used C.
def makeCubes(arr):
ans = []
for ar in arr:
ar2 = [ar[i:i + 2] for i in range(0, len(ar), 2)]
#splitting into segments of 2 each
if not '00' in ar2:
ans += [ar2]
return ans
class Node:
def __init__(self,key):
self.key = key
self.left = None
self.right = None
def addLeft(self,node):
self.left = node
def addRight(self,node):
self.right = node
def makeTree(size):
if(size == 1):
leaf = Node('x0')
leaf.addLeft(Node('zero'))
leaf.addRight(Node('zero'))
return leaf
else:
node = Node('x'+str(size-1))
childNode = makeTree(size-1)
node.addLeft(childNode)
node.addRight(childNode)
return node
def inOrder(root):
if(root != None):
return inOrder(root.left) + [root.key] + inOrder(root.right)
return []
def makeOBDD(array):
maxLen = max([len(word) for word in array])
tree = makeTree(maxLen)
for cube in array:
tree = makeOne(tree,cube)
return tree
def makeOne(root,cube):
print("cube",cube)
if(cube == []):
print("updated")
root.key = 'one'
else:
element = cube[0]
if(element == '01'):
root.addLeft(makeOne(root.left,cube[1:]))
elif(element == '10'):
root.addRight(makeOne(root.right,cube[1:]))
return root
# ab + a'b'
'''
Expected output
x1
/ \
x0 x0
/ \ / \
1 0 0 1
'''
cubeSet = ['1010','0101']
cubes = makeCubes(cubeSet)
print(cubes)
obdd = makeOBDD(cubes)
print(inOrder(obdd))

Binary Search Tree - Finding the size

I want to find the size of the tree with a given node which will be stated like this
print bst.get("B")
However, when I tried to print out, it keeps stating that "it only accept 1 argument but 2 is given"
Sorry, can someone help me out, as I'm quite new to this.
the brief code is:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
The full code:
import os
import pygraphviz as pgv
from collections import deque
class BST:
root=None
def put(self, key, val):
self.root = self.put2(self.root, key, val)
def put2(self, node, key, val):
if node is None:
#key is not in tree, create node and return node to parent
return Node(key, val)
if key < node.key:
# key is in left subtree
node.left = self.put2(node.left, key, val)
elif key > node.key:
# key is in right subtree
node.right = self.put2(node.right, key, val)
else:
node.val = val
# node.count = 1 + self.size2(node.left) + self.size2(node.right)
return node
# draw the graph
def drawTree(self, filename):
# create an empty undirected graph
G=pgv.AGraph('graph myGraph {}')
# create queue for breadth first search
q = deque([self.root])
# breadth first search traversal of the tree
while len(q) <> 0:
node = q.popleft()
G.add_node(node, label=node.key+":"+str(node.val))
if node.left is not None:
# draw the left node and edge
G.add_node(node.left, label=node.left.key+":"+str(node.left.val))
G.add_edge(node, node.left)
q.append(node.left)
if node.right is not None:
# draw the right node and edge
G.add_node(node.right, label=node.right.key+":"+str(node.right.val))
G.add_edge(node, node.right)
q.append(node.right)
# render graph into PNG file
G.draw(filename,prog='dot')
os.startfile(filename)
def createTree(self):
self.put("F",6)
self.put("D",4)
self.put("C",3)
self.put("B",2)
self.put("A",1)
self.put("E",5)
self.put("I",9)
self.put("G",7)
self.put("H",8)
self.put("J",10)
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size(temp.left) + self.size(temp.right)
def size2(self,n):
if n is None:
return 0
else:
return 1 + self.size2(n.left) + self.size2(n.right)
class Node:
left = None
right = None
key = 0
val = 0
def __init__(self, key, val):
self.key = key
self.val = val
bst = BST()
bst.createTree()
bst.drawTree("demo.png")
##print bst.size("D")
I get a stack overflow with your code. I think you need to use size2 in your size method:
def size(self,key):
temp = self.root
if (temp == 0):
return 0
return 1 + self.size2(temp.left) + self.size2(temp.right)
Personally, I would maybe not call the method size2, but that's a matter of taste (and style). Also, the key seems to be unused?

Printing the tree path

I am learning python as one my project requirements is to print a binary tree.
I am trying out my code to print the tree path.
It just stops at root node.
I am trying to find where I messed up.
Tree:
A
/ \
B C
/ / \
D E F
The output I want is:
ABD
ACE
ACF
class Node(object):
def __init__(self, data):
self.data = data
self.children = []
self.val = data
self.left = None
self.right = None
r = Node('A')
r.left = Node('B')
r.right = Node('C')
r.left.left = Node('D')
r.right.right = Node('E')
The above should be r.right.left = Node('E)
r.right.right = Node('F')
def binaryTreePaths(root):
results = []
c = []
binary_tree_paths(root, c, results)
return results
def binary_tree_paths(root, cand, res):
if root is None:
return
else:
cand.append(str(root.val)+" ")
if root.left is None and root.right is None:
p = ''.join(map(str, cand))
res.append(p)
binary_tree_paths(root.left, cand, res)
binary_tree_paths(root.right, cand, res)
cand.pop()
print binaryTreePaths(r)
You should have r.right.left = Node('E'), instead of r.right.right = Node('E')

Categories