Python: Build Binary Tree with Pre and Inorder - python

I need help completing the recursive part of my function. The function is supposed to use my ListBinaryTree Class to help reconstruct a tree given its inorder and preorder traversal in string format: eg.
preorder = '1234567'
inorder = '3241657'
def build_tree(inorder, preorder):
head = preorder[0]
print(head)
head_pos = inorder.index(head)
print(head_pos)
left_in = inorder[:head_pos]
print(left_in)
right_in = inorder[(head_pos+1):]
print(right_in)
left_pre = preorder[1:-len(right_in)]
print(left_pre)
right_pre = preorder[-len(right_in):]
print(right_pre)
Which finds the important values in the preorder and inorder traversals and splits the tree up to determine which numbers are for the left side and right side of the tree.
An example of its input and output is:
build_tree('3241657', '1234567')
.
1
3
324
657
234
567
My class that I use to create the tree is as follows:
class ListBinaryTree:
"""A binary tree class with nodes as lists."""
DATA = 0 # just some constants for readability
LEFT = 1
RIGHT = 2
def __init__(self, root_value, left=None, right=None):
"""Create a binary tree with a given root value
left, right the left, right subtrees
"""
self.node = [root_value, left, right]
def create_tree(self, a_list):
return ListBinaryTree(a_list[0], a_list[1], a_list[2])
def insert_value_left(self, value):
"""Inserts value to the left of this node.
Pushes any existing left subtree down as the left child of the new node.
"""
self.node[self.LEFT] = ListBinaryTree(value, self.node[self.LEFT], None)
def insert_value_right(self, value):
"""Inserts value to the right of this node.
Pushes any existing left subtree down as the left child of the new node.
"""
self.node[self.RIGHT] = ListBinaryTree(value, None, self.node[self.RIGHT])
def insert_tree_left(self, tree):
"""Inserts new left subtree of current node"""
self.node[self.LEFT] = tree
def insert_tree_right(self, tree):
"""Inserts new left subtree of current node"""
self.node[self.RIGHT] = tree
def set_value(self, new_value):
"""Sets the value of the node."""
self.node[self.DATA] = new_value
def get_value(self):
"""Gets the value of the node."""
return self.node[self.DATA]
def get_left_subtree(self):
"""Gets the left subtree of the node."""
return self.node[self.LEFT]
def get_right_subtree(self):
"""Gets the right subtree of the node."""
return self.node[self.RIGHT]
def __str__(self):
return '['+str(self.node[self.DATA])+', '+str(self.node[self.LEFT])+', '+\
str(self.node[self.RIGHT])+']'
For the recursive part of the function I tried doing something like:
my_tree= ListBinaryTree(head)
while my_tree.get_value() != None:
left_tree = build_tree(left_in, left_pre)
right_tree = build_tree(right_in, right_pre)
my_tree.insert_value_left(left_tree)
my_tree.insert_value_right(right_tree)
print (my_tree)
But it returns an "index out of range" error.
Also for something like:
def build_tree(inorder, preorder):
head = preorder[0]
head_pos = inorder.index(head)
left_in = inorder[:head_pos]
right_in = inorder[(head_pos+1):]
left_pre = preorder[1:-len(right_in)]
right_pre = preorder[-len(right_in):]
if left_in:
left_tree = build_tree(left_in, left_pre)
else:
left_tree = None
if right_in:
right_tree = build_tree(right_in, right_pre)
else:
right_tree = None
my_tree = ListBinaryTree(head, left_tree, right_tree)
print(my_tree)
input
build_tree('3241657', '1234567')
returns
[3, None, None]
[4, None, None]
[2, None, None]
[6, None, None]
[7, None, None]
[5, None, None]
[1, None, None]
Can anyone please help me with the recursive part?
Thanks

You're making the recursive part much harder than necessary.
if left_in:
left_tree = build_tree(left_in, left_pre)
else:
left_tree = None
if right_in:
right_tree = build_tree(right_in, right_pre)
else:
right_tree = None
return ListBinaryTree(head, left_tree, right_tree)
You could perhaps simplify it even further by moving the checks for empty sequences up to the top of the function (e.g. if not inorder: return None) so it only needs to appear once.

Related

Find matrix row elements in a binary tree

I am trying to write a function which given a binary search tree T of integers and a rectangular matrix M of integers, verify if there exists a row of M whose values belong to T.
This is my code:
M = [
[1, 2, 3],
[4, 5, 6]
]
class Tree:
def __init__(self, root=None, left=None, right=None):
self.root = root
self.left = left
self.rigth = right
def FindRowInTree(T, M):
if T is None:
return False
else:
for r in M:
for e in r:
if e == T.root and FindRowInTree(T.left, M) is True and FindRowInTree(T.right, M) is True:
return True
FindRowInTree(T.left, M) and FindRowInTree(T.right,M)
return False
t = Tree(4, Tree(5, None, None), Tree(6, None, None))
x = FindRowInTree(t, M)
print(x)
It always returns False.
What would I need to change to make it work properly?
Break the problem into pieces. First write a function to find a single value in the tree:
class Tree:
def __init__(self, root=None, left=None, right=None):
self.root = root
self.left = left
self.right = right
def __contains__(self, value):
return (
self.root == value
or self.left is not None and value in self.left
or self.right is not None and value in self.right
)
Note that with an ordered binary tree, you could make this function more efficient by having it only check left or right depending on how the value you're looking for compares to the root value; that's what a "binary search" is. Since your tree is unordered, though, you just need to search both children at each node, meaning you're traversing the entire tree.
In any case, once you have a function that looks up a single value, all you need to do is call it in a loop:
def find_row_in_tree(tree, matrix):
return any(
all(i in tree for i in row)
for row in matrix
)
If you're trying to do this in a more efficient way, an unordered binary tree is not doing you any favors; I'd just write a utility function to convert it to something more useful, like a set:
def tree_to_set(tree):
if tree is None:
return set()
return {tree.root} | tree_to_set(tree.left) | tree_to_set(tree.right)
def find_row_in_tree(tree, matrix):
tree_as_set = tree_to_set(tree)
return any(tree_as_set.issuperset(row) for row in matrix)

How do I traverse binary tree made up of nodes? (code inside)

I am given a class that creates a binary tree filled with nodes.each node is given a parent and a pointer to its left or right child.
Binary tree node class:
class BTNode():
''' a class that represents a binary tree node'''
def __init__(self, data, parent=None, left_child=None, right_child=None):
'''(BTNode, obj, BTNode, BTNode, BTNode) -> NoneType
Constructs a binary tree nodes with the given data'''
self._parent = parent
self._left = left_child
self._data = data
self._right = right_child
def set_parent(self, parent):
'''(BTNode, BTNode) -> NoneType
set the parent to the given node'''
self._parent = parent
def set_left(self, left_child):
'''(BTNode, BTNode) -> NoneType
set the left child to the given node'''
self._left = left_child
def set_right(self, right_child):
'''(BTNode, BTNode) -> NoneType
set the right child to the given node'''
self._right = right_child
def set_data(self, data):
'''(BTNode, obj) -> NoneType
set the data at this node to the given data'''
self._data = data
def get_parent(self):
'''(BTNode) -> BTNode
return the pointer to the parent of this node'''
return self._parent
def get_left(self):
'''(BTNode) -> BTNode
return the pointer to the left child'''
return self._left
def get_right(self):
'''(BTNode) -> BTNode
return the pointer to the right child'''
return self._right
def get_data(self):
'''(BTNode) -> obj
return the data stored in this node'''
return self._data
def has_left(self):
'''(BTNode) -> bool
returns true if this node has a left child'''
return (self.get_left() is not None)
def has_right(self):
'''(BTNode) -> bool
returns true if this node has a right child'''
return (self.get_right() is not None)
def is_left(self):
'''(BTNode) -> bool
returns true if this node is a left child of its parent'''
# you need to take care of exception here, if the given node has not parent
return (self.get_parent().get_left() is self)
def is_right(self):
'''(BTNode) -> bool
returns true if the given node is a right child of its parent'''
# you need to take care of exception here, if the given node has not parent
return (self.get_parent().get_right() is self)
def is_root(self):
'''(BTNode) -> bool
returns true if the given node has not parent i.e. a root '''
return (self.get_parent() is None)
code example of how to create a tree:
''' create this BT using BTNode
A
/ \
B C
/\ \
D E F
/
G
'''
node_G = BTNode("G")
node_F = BTNode("F", None,node_G)
node_G.set_parent(node_F)
node_C = BTNode("C", None, None, node_F)
node_F.set_parent(node_C)
node_D = BTNode("D")
node_E = BTNode("E")
node_B = BTNode("B",None, node_D, node_E)
node_D.set_parent(node_B)
node_E.set_parent(node_B)
node_A = BTNode("A",None, node_B, node_C)
node_B.set_parent(node_A)
I dont know how to traverse this tree. I was suggested using recursion but Im not sure how. For example, I need to return True if the tree differs in height by at most 1 level,so the tree above would return true. How do I do this? Thanks!
Try to think recursively. Let's start off with a few definitions.
A tree is balanced if its left and right trees have the same height and each of it's subtrees is balanced. Also we will define an empty tree as being balanced.
The height of a tree, h(t) = 1 + max(h(t.left), h(t.right)). In English, the height of a tree is 1 + the height of its taller child tree. Also we will assume that an empty tree has a height of 0.
So for every node in the tree we can check the height of both of its children and compare them. If they aren't equal we know the tree is not balanced and we return false.
Let's start by defining the code to check if a tree is balanced.
def is_balanced(node):
if node is None:
return True
left_height = get_height(node.get_left())
right_height = get_height(node.get_right())
return left_height == right_height and is_balanced(node.get_left()) and is_balanced(node.get_right())
Now let's define the function get_height that we used above. Since the height of a tree is a function of a height of it's subtrees we can use recursion. Since recursion requires a base case so we do not recurse infinitely we can use the fact that an empty tree has a height of 0.
def get_height(node):
if node is None:
return 0 # Assuming empty tree has a height of 0
return 1 + max(get_height(node.get_left()), get_height(node.get_right()))
Now to put it all together we can recursively iterate through the tree and check that every node is balanced by calling is_balanced on the root.
is_balanced(node_A)
BONUS Exercise:
The code I gave you will work but it won't scale well. If the tree gets very large it will run much slower. Why is it slow and what can you do to make it faster?
You can traverse the left and right sides of the tree to find the maximum path length to a leaf:
class Tree:
def __init__(self, **kwargs):
self.__dict__ = {i:kwargs.get(i) for i in ['value', 'left', 'right']}
def get_length(self, current=[]):
yield current+[1]
yield from getattr(self.left, 'get_length', lambda _:[])(current+[1])
yield from getattr(self.right, 'get_length', lambda _:[])(current+[1])
def right_length(self):
return len(max(getattr(self.right, 'get_length', lambda :[[]])(), key=len))
def left_length(self):
return len(max(getattr(self.left, 'get_length', lambda :[[]])(), key=len))
t = Tree(value = 'A', left=Tree(value='B', left=Tree(value='D'), right=Tree(value='E')), right = Tree(value='C', left = Tree(value='F', left=Tree(value='G'))))
print(t.right_length() - t.left_length())
Output:
1

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.

Reconstructing a tree using inorder and preorder traversals in Python

I need to make a function that builds a tree from a preorder and inorder traversal, but I'm not sure where I should put the MakeNode and recursive calls to construct the tree properly.
Assuming inorder and preorder are both a list of ints, is the following algorithm correct to reconstruct the tree using the traversals?
def build(preorder, inorder):
root = preorder[0]
left subtree = inorder[:root-1]
right subtree = inorder[root+1:]
If so - How can one take that and construct a heap (ArrayHeap) using that algorithm recursively?
I have a class designed to construct nodes and then I can simply use heap.add(node) to create the heap.
Say my class to build a node is named "MakeNode" and is constructed as follows (for syntax purposes):
Class MakeNode():
def __init__(self, character, left=None, right=None):
To create the root node I would need to edit the function like this:
def build(preorder, inorder, heap):
root = preorder[0]
node = MakeNode(root) # Creating root node here
heap.add(node) # Adding to heap
left subtree = inorder[:root-1]
right subtree = inorder[root+1:]
But how should I use recursion to build the rest of the tree?
I can incorporate the left and right preorder for ordering purposes by doing this:
def build(preorder, inorder, heap):
root = preorder[0]
node = MakeNode(root)
heap.add(node)
left subtree = inorder[:root-1]
# order of left subtree = preorder[1:1+left subtree]
right subtree = inorder[root+1:]
# order of right subtree = preorder[root+1:]
I don't really know how to incorporate the recursive calls to build the tree or what exactly to put for the left or right parameters when doing so.
If anybody has any suggestions I would appreciate them, and I'm sorry if I've been unclear.
What you need to do is add the root of the pre-ordered list to the tree, and removing it from preorder list. Split the in order list as you are doing, then passing both the left and right branches recursively. Keep adding the first element of pre-order to the left of the previous node unless left_subtree is empty, then you need to add it to the right.
This is python code (already tested):
class Tree():
def __init__(self, inorder, preorder):
self.root = preorder[0]
lt = inorder[:inorder.index(self.root)]
rt = inorder[inorder.index(self.root) + 1:]
self.build(self.root, lt, rt, preorder[1:])
def build(self, last_node, left_subtree, right_subtree, preorder):
left_preorder = [node for node in preorder if node in left_subtree]
right_preorder = [node for node in preorder if node in right_subtree]
if len(left_subtree) > 0:
last_node.left = left_preorder[0]
lt = left_subtree[:left_subtree.index(last_node.left)]
rt = left_subtree[left_subtree.index(last_node.left) + 1:]
self.build(last_node.left, lt, rt, left_preorder)
if len(right_subtree) > 0:
last_node.right = right_preorder[0]
lt = right_subtree[:right_subtree.index(last_node.right)]
rt = right_subtree[right_subtree.index(last_node.right) + 1:]
self.build(last_node.right, lt, rt, right_preorder)
From http://www.cse.hut.fi/en/research/SVG/TRAKLA2/tutorials/heap_tutorial/taulukkona.html, you have:
parent(i) = i/2
left(i) = 2i
right(i) = 2i+1
so you can define a class:
class ArrayHeapNode:
def __init__(self, elements, index):
self.elements = elements
self.index = index
def left(self):
next = self.index * 2
if next >= len(self.elements):
return None
return ArrayHeapNode(self.elements, next)
def right(self):
next = (self.index * 2) + 1
if next >= len(self.elements):
return None
return ArrayHeapNode(self.elements, next)
def value(self):
return self.elements[self.index]
def set_value(self, _value):
self.elements[self.index] = _value
This gives you a class that can then function as a tree on an array representation of a binary heap according to the article. If there is no element in that branch, None is returned.
You can now create tree traversal algorithms (https://en.wikipedia.org/wiki/Tree_traversal):
def inorder_traversal(node, action):
if not node: return
inorder_traversal(node.left(), action)
action(node.value())
inorder_traversal(node.right(), action)
def preorder_traversal(node, action):
if not node: return
action(node.value())
preorder_traversal(node.left(), action)
preorder_traversal(node.right(), action)
These will also work with a traditional binary tree node:
class BinaryTreeNode:
def __init__(self, value, left, right):
self._value = value
self._left = left
self._right = right
def left(self):
return self._left
def right(self):
return self._right
def value(self):
return self._value
def set_value(self, _value):
self._value = _value
Now, you can make the traversal algorithms more flexible and python-like by doing:
def inorder(node):
if node:
for item in inorder(node.left()):
yield item
yield node.value()
for item in inorder(node.right()):
yield item
This allows you to write things like:
for item in inorder(tree):
print item
You can then count the elements from the node by doing:
n = sum(1 for e in inorder(root))
This then allows you to create an empty array capable of holding n elements for the elements in the heap:
elements = [0 for x in range(n)]
or combined:
elements = [0 for x in inorder(root)]
heap = ArrayHeapNode(elements, 0)
Now, you can iterate over both trees at the same time using:
for a, b in zip(inorder(root), inorder(heap)):
b = a
This should then assign all elements in the binary tree (root) to the correct elements in the array heap (heap) using inorder traversal. The same can be done by implementing a preorder function.
NOTE: I have not tested this code.
def pre_order(node):
print node #pre order ... print ourself first
pre_order(node.left)
pre_order(node.right)
def post_order(node):
post_order(node.left)
post_order(node.right)
print node # post order print self last...
def in_order(node):
in_order(node.left)
print node #in order ... between its children
in_order(node.right)
if you have any one of these you should be able to reproduce the tree
assume we have a tree like this
0
1 2
3 4 5 6
our traversals would be
0,1,3,4,2,5,6 #pre order
3,1,4,0,5,2,6 #in order
so from this we know that
zero is our root
3 is our left most deepest node
6 is our right most deepest node
0
...
3 6
our left over nodes are
1,4,2,5 # preorder
1,4,5,2 # in-order
from this we know that
1 is a child of zero
1 is the next level leftmost node
2 is the next level rightmost node
so we now have
0
1 2
3 6
leaving us with
4,5 # pre order
4,5 # in order
from this we know that 4 is a child of 1, and therefore 5 must be a child of 2 ...
now write a function that does all that
this article may help
http://leetcode.com/2011/04/construct-binary-tree-from-inorder-and-preorder-postorder-traversal.html

Using extend correctly?

class BTNode(object):
"""A node in a binary tree."""
def __init__(self, item, left=None, right=None):
"""(BTNode, object, BTNode, BTNode) -> NoneType
Initialize this node to store item and have children left and right,
as well as depth 0.
"""
self.item = item
self.left = left
self.right = right
self.depth = 0 # the depth of this node in a tree
def __str__(self):
"""(BTNode) -> str
Return a "sideways" representation of the subtree rooted at this node,
with right subtrees above parents above left subtrees and each node on
its own line, preceded by as many TAB characters as the node's depth.
"""
# If there is a right subtree, add an extra TAB character in front of
# every node in its string representation, with an extra newline at the
# end (ready to be concatenated with self.item).
if self.right:
str_right = "\t" + str(self.right).replace("\n", "\n\t") + "\n"
else:
str_right = ""
# The string representation of self.item: if item is a string, use
# repr(item) so that quotes are included and special characters are
# treated correctly -- just like in a list; else use str(item).
if isinstance(self.item, str):
str_item = repr(self.item)
else:
str_item = str(self.item)
# If there is a left subtree, add an extra TAB character in front of
# every node in its string representation, with an extra newline at the
# beginning (ready to be concatenated with self.item).
if self.left:
str_left = "\n\t" + str(self.left).replace("\n", "\n\t")
else:
str_left = ""
# Put the right subtree above the root above the left subtree.
return str_right + str_item + str_left
def leaves_and_internals(self):
"""(BTNode) -> ([object], [object])
Return a pair of lists: (list of all the items stored in the leaves of
the subtree rooted at this node, list of all the items stored in the
internal nodes of the subtree rooted at this node).
"""
#Leaf: Doesn't have children
#Internal Node: Has children
leaf = []
internal = []
if not self.left and not self.right:
leaf.append(self.item)
if self.left is not None:
internal.append(self.item)
if self.right is not None:
self.right.leaves_and_internals()
self.left.leaves_and_internals()
elif self.right is not None:
internal.append(self.item)
self.right.leaves_and_internals()
print(leaf, internal)
return leaf, internal
The BTNode class is starter code given to us where we cannot edit. My responsibility is to write code for leaves_and_intervals by following the docstring given. My only problem currently is not knowing how to use extend to properly combine lists recursively.
My test block looks like so:
if __name__ == "__main__":
root = BTNode(2, BTNode(4, BTNode(6, BTNode(8, None, None), None), None), None)
root.leaves_and_internals()
And my output looks like so:
[8] []
[] [6]
[] [4]
[] [2]
The correct output should look like so (order should not matter):
[8] [6, 4, 2]
How do I correctly use extend so that leaf and internal don't keep resetting back to empty list every time it recurses? I know I must have a second set of lists for extend, just not sure how to implement it.
You do not need to explicitly declare empty list if you want to recursively solve this problem.
Just aggregate each sub-problem's result.
Here is my approach:
def leaves_and_internals(self):
if not self.left and not self.right:
#leaf.append(self.item)
return ([], [self.item])
if self.left is not None:
#internal.append(self.item)
if self.right is not None:
#return ([self.item], []) + self.right.leaves_and_internals()
return tuple(map(operator.add,([self.item], []), self.right.leaves_and_internals()))
#return ( [self.item], []) + self.left.leaves_and_internals()
return tuple( map(operator.add, ([self.item], []), self.left.leaves_and_internals()))
elif self.right is not None:
#internal.append(self.item)
#(leaf, internal) + self.right.leaves_and_internals()
#return ( [self.item], []) + self.right.leaves_and_internals()
return tuple(map(operator.add,([self.item], []), self.right.leaves_and_internals()))
I see that your code almost has the correct logic. Here is my implementation.
def leaves_and_internals(self):
leaves = []
internals = []
if self.left or self.right:
internals.append(self.item)
else:
leaves.append(self.item)
if self.left:
sub_leaves, sub_internals = self.left.leaves_and_internals()
leaves.extend(sub_leaves)
internals.extend(sub_internals)
if self.right:
sub_leaves, sub_internals = self.right.leaves_and_internals()
leaves.extend(sub_leaves)
internals.extend(sub_internals)
return leaves, internals

Categories

Resources