python create a binary search tree using existing function - python

I'm practicing creating a balanced binary search tree in python.
I already have these below, any idea on how to create a balance_bst funtion that passed a list of unique values that are
sorted in increasing order. It returns a reference to the root of a well-balanced binary search tree:
class LN:
def __init__(self,value,next=None):
self.value = value
self.next = next
def list_to_ll(l):
if l == []:
return None
front = rear = LN(l[0])
for v in l[1:]:
rear.next = LN(v)
rear = rear.next
return front
def str_ll(ll):
answer = ''
while ll != None:
answer += str(ll.value)+'->'
ll = ll.next
return answer + 'None'
# Tree Node class and helper functions (to set up problem)
class TN:
def __init__(self,value,left=None,right=None):
self.value = value
self.left = left
self.right = right
def height(atree):
if atree == None:
return -1
else:
return 1+ max(height(atree.left),height(atree.right))
def size(t):
if t == None:
return 0
else:
return 1 + size(t.left) + size(t.right)
def is_balanced(t):
if t == None:
return True
else:
return abs(size(t.left)-size(t.right)) <= 1 and is_balanced(t.left) and is_balanced(t.right)
def str_tree(atree,indent_char ='.',indent_delta=2):
def str_tree_1(indent,atree):
if atree == None:
return ''
else:
answer = ''
answer += str_tree_1(indent+indent_delta,atree.right)
answer += indent*indent_char+str(atree.value)+'\n'
answer += str_tree_1(indent+indent_delta,atree.left)
return answer
return str_tree_1(0,atree)
How do write the balance_bst?
def balance_bst(l):
Here is what I did:
def build_balanced_bst(l):
if l == None:
return None
else:
middle = len(l) // 2
return TN(l[middle],
build_balanced_bst(l[:middle]),
build_balanced_bst(l[middle + 1:]))
It gives me:
IndexError: list index out of range
How do I fix it?

I'm not going to write it for you since that's not what SO is about, but here's the general idea. Since the list is already sorted, the root should be the element in the middle of the list. Its left child will be the root of the balanced tree consisting of the elements to the left of the root in the list, and the right sub-tree will be the rest.

Related

Trouble balancing a binary search tree using AVL

I'm unable to identify where I'm going wrong with my AVL implementation for balancing an existing binary search tree. I'm not getting any errors but my binary search tree does not come out to be properly balanced. After insertion, my binary search tree looks like (it would be prudent here to mention that my display_keys method gives us a visualization that is rotated by 90 degrees):
∅
The-Dreamers
∅
Saint-Laurent
∅
Pierrot-le-Fou
∅
Contempt
Cold-War
Before-Sunrise
∅
Basic-Instinct
∅
This is correct as it seems be to be following the rules for a BST.
But after calling the BalanceTree() method on my binary search tree, I seem to get:
∅
The-Dreamers
Saint-Laurent
Pierrot-le-Fou
Contempt
Cold-War
Before-Sunrise
Basic-Instinct
∅
which as you can see, is not Balanced. But wait, and here's the catch, if I call BalanceTree() again, the tree comes out to be perfectly balanced and isBSTBalanced returns True also. After the second call to BalanceTree(), our tree looks like:
∅
The-Dreamers
Saint-Laurent
Pierrot-le-Fou
Contempt
Cold-War
Before-Sunrise
Basic-Instinct
∅
I am adding the complete source code for my BST class for clarity and if somebody wants to execute the code, but I have added a comment (#Addition of new methods for AVL starts here) in the BST class to indicate where the methods for AVL start. You need only concern yourself with them. I would like for you help me pinpoint what exactly is going wrong in my code.
class BST:
class TreeNode:
def __init__(self, key, value, left=None, right=None, parent=None):
self.key = key
self.value = value
self.left = left
self.right = right
self.parent = parent
self.height = 1
def __init__(self):
self.root = None
self.size = 0
def __len__(self):
return self.size
def insert(self, key, value):
if self.root == None:
self.root = self.TreeNode(key, value)
else:
self._insert(key, value, self.root)
self.size += 1
def _insert(self, key, value, curr_node):
if key < curr_node.key:
if curr_node.left is not None:
self._insert(key, value, curr_node.left)
else:
curr_node.left = self.TreeNode(key, value, parent=curr_node)
elif key > curr_node.key:
if curr_node.right is not None:
self._insert(key, value, curr_node.right)
else:
curr_node.right = self.TreeNode(key, value, parent=curr_node)
def search(self, key):
if self.root:
found = self._search(key, self.root)
if found:
return found.value
else:
return None
else:
return None
def _search(self, key, curr_node):
if not curr_node:
return None
elif curr_node.key == key:
return curr_node
elif key < curr_node.key:
return self._search(key, curr_node.left)
else:
return self._search(key, curr_node.right)
def find_min(self):
curr = self.root
while curr.left is not None:
curr = curr.left
return curr
def find(self, node):
curr = node
while curr.left is not None:
curr = curr.left
return curr
def delete(self, key):
node_to_remove = self._search(key, self.root)
if node_to_remove.left is None and node_to_remove.right is None:
#Then we identify this as a leaf node
if node_to_remove is node_to_remove.parent.left:
#Setting the parent's reference to this to None
node_to_remove.parent.left = None
elif node_to_remove is node_to_remove.parent.right:
node_to_remove.parent.right = None
#2nd Case --> Two child
elif node_to_remove.left and node_to_remove.right:
minimum = self.find(node_to_remove.right)
self.delete(minimum.key) #We will still have a ref to this node afterwards
node_to_remove.key, node_to_remove.value = minimum.key, minimum.value
#3rd Case -> One child
else:
if node_to_remove.left:
node_to_remove.left.parent = node_to_remove.parent
node_to_remove.parent.left = node_to_remove.left
elif node_to_remove.right:
node_to_remove.right.parent = node_to_remove.parent
node_to_remove.parent.right = node_to_remove.right
def traversal(self, root):
res = []
if root:
res = self.traversal(root.left)
res.append(root)
res = res + self.traversal(root.right)
return res
def inorder_traversal(self, root):
if root:
self.inorder_traversal(root.left)
print(root.key)
self.inorder_traversal(root.right)
#Addition of new methods for AVL starts here
def display_keys(self, node, space='\t', level=0):
"""
Allows us to visualize the tree (albiet rotated by 90 degrees)
"""
# print(node.key if node else None, level)
# If the node is empty
if node is None:
print(space*level + '∅')
return
# If the node is a leaf
if node.left is None and node.right is None:
print(space*level + str(node.key))
return
# If the node has children
self.display_keys(node.right, space, level+1)
print(space*level + str(node.key))
self.display_keys(node.left,space, level+1)
def height(self):
return self._height(self.root)
def _height(self, curr_node):
if curr_node is None:
return -1 #since we are counting number of edges, we will return -1
else:
return 1 + max(self._height(curr_node.left), self._height(curr_node.right))
def isBSTBalanced(self):
return self._isBSTBalanced(self.root)
def _isBSTBalanced(self, curr_node):
if curr_node is None:
return True
hleft_subtree = self._height(curr_node.left)
hright_subtree = self._height(curr_node.right)
if hleft_subtree - hright_subtree in [-1,0,1]:
return self._isBSTBalanced(curr_node.left) and self._isBSTBalanced(curr_node.right)
else:
return False
def balance_factor(self):
if self.root is not None:
return self._balance_factor(self.root)
else:
return 0
def _balance_factor(self, curr_node):
if curr_node is None:
return
hleft_subtree = self._height(curr_node.left)
hright_subtree = self._height(curr_node.right)
b_factor = hleft_subtree - hright_subtree
return b_factor
def BalanceTree(self):
if self.isBSTBalanced() == False:
return self._rebalance(self.root)
def _rebalance(self, curr_node):
if curr_node is None:
return None
curr_node.left = self._rebalance(curr_node.left)
curr_node.right = self._rebalance(curr_node.right)
curr_node.height = 1 + max(self._height(curr_node.left), self._height(curr_node.right))
#print(curr_node.height)
if self._balance_factor(curr_node) > 1 and self._balance_factor(curr_node.left) >= 0:
#left heavy subtree
return self._rotate_right(curr_node)
if self._balance_factor(curr_node) < -1 and self._balance_factor(curr_node.right) <= 0:
#right heavy subtree
return self._rotate_left(curr_node)
if self._balance_factor(curr_node) < 0 and self._balance_factor(curr_node.right) > 0:
self._rotate_right(curr_node.right)
return self._rotate_left(curr_node)
if self._balance_factor(curr_node) > 0 and self._balance_factor(curr_node.left) < 0:
self._rotate_left(curr_node.left)
return self._rotate_right(curr_node)
return curr_node
def _rotate_left(self, oldRoot):
newRoot = oldRoot.right #the newRoot is the right child of the previous root
oldRoot.right = newRoot.left #replacing right child of the old root with the left child of the new
if newRoot.left is not None:
newRoot.left.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot == self.root:
self.root = newRoot
else:
if oldRoot.parent.left is oldRoot: #Checking isLeftChild
oldRoot.parent.left = newRoot
else:
oldRoot.parent.right = newRoot
newRoot.left = oldRoot
oldRoot.parent = newRoot
oldRoot.height = 1 + max(self._height(oldRoot.left), self._height(oldRoot.right))
newRoot.height = 1 + max(self._height(newRoot.left), self._height(newRoot.right))
return newRoot
def _rotate_right(self, oldRoot):
newRoot = oldRoot.left #the newRoot is the left child of the previous root
oldRoot.left = newRoot.right #replacing left child of the old root with the right child of the new
if newRoot.right is not None:
newRoot.right.parent = oldRoot
newRoot.parent = oldRoot.parent
if oldRoot == self.root:
self.root = newRoot
else:
if oldRoot.parent.right is oldRoot: #Checking isRightChild
oldRoot.parent.right = newRoot
else:
oldRoot.parent.left = newRoot
newRoot.right = oldRoot
oldRoot.parent = newRoot
oldRoot.height = 1 + max(self._height(oldRoot.left), self._height(oldRoot.right))
newRoot.height = 1 + max(self._height(newRoot.left), self._height(newRoot.right))
return newRoot
if __name__ == '__main__':
obj = BST()
obj.insert('Basic-Instinct', 0)
obj.insert('The-Dreamers', 1)
obj.insert('Saint-Laurent', 2)
obj.insert('Pierrot-le-Fou', 3)
obj.insert('Contempt', 4)
obj.insert('Before-Sunrise', 5)
obj.insert('Cold-War', 8)
obj.display_keys(obj.root) #displays a visual representation of our tree, albeit rotated by 90 degrees
print()
print("isBSTBalanced:", obj.isBSTBalanced())
obj.BalanceTree()
print("isBSTBalanced:", obj.isBSTBalanced()) #After executing BalanceTree(), isBSTBalanced still returns False
print()
obj.display_keys(obj.root)
Progress: Revamped _isBSTBalanced method so that it visits every node recursively and not just the root node. The final outcome, however, remains the same.
Progress: I was able to identify one of the major issues being that while I was calling _rotate_left and _rotate_right methods in the _rebalance method, I was not returning them. In addition to this, I was not recursively visiting the left and right subtrees of curr_node, which was initially set to the root of the tree, to be able to traverse the tree in a bottom up manner. I have resolved this too. I have added a display_keys method which allows us to visualize the tree, albeit rotated by 90 degrees. I'm updating the code and prompt in this post accordingly. The problem that still remains is that I have to call the BalanceTree() method more than once in some cases for isBSTBalanced to return True.

Why is my helper method not activating recursively?

I have a Binary Search Tree and I am trying to trace recursively in order through the tree and append each key,value to a list. It is only appending the first key,value to the list and not going through the list in order. I pasted my code below, along with the test code I used at the bottom. Any help on how to get past this issue is super appreciated!
class TreeMap:
class Node:
def __init__(self, key, value):
self.key = key
self.value = value
self.left = None
self.right = None
def __init__(self):
self.root = None
self.numsearches = 0
self.numcomparisons = 0
def add(self, newkey, newvalue):
newkey = newkey.lower()
if self.root == None:
self.root = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(self.root, newkey, newvalue)
def add_helper(thisnode, newkey, newvalue):
if newkey <= thisnode.key:
if thisnode.left == None:
thisnode.left = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(thisnode.left, newkey, newvalue)
else:
if thisnode.right == None:
thisnode.right = TreeMap.Node(newkey, newvalue)
else:
TreeMap.add_helper(thisnode.right, newkey, newvalue)
def print(self):
TreeMap.print_helper(self.root, 0)
def print_helper(somenode, indentlevel):
if somenode == None:
print(" "*(indentlevel),"---")
return
if not TreeMap.isleaf(somenode):
TreeMap.print_helper(somenode.right, indentlevel + 5)
print(" "*indentlevel + str(somenode.key) + ": " +str(somenode.value))
if not TreeMap.isleaf(somenode):
TreeMap.print_helper(somenode.left, indentlevel + 5)
def isleaf(anode):
return anode.left == None and anode.right == None
def listify(self, whichorder="in"):
'''
Returns a list consisting of all the payloads of the tree. (This returns a plain old Python List.)
The order of the payloads is determined by whichorder, which defaults to inorder.
The other possibilities are "pre" and "post".
If the tree is empty, return the empty list.
'''
assert type(whichorder) is str,"Whichorder is a string, and can only be pre, in or post"
assert whichorder in ["pre","in","post"],"Whichorder is a string, and can only be pre, in or post"
return TreeMap.listify_helper(self.root, whichorder)
def listify_helper(somenode, whichorder):
order_list = []
if somenode == None:
return order_list
elif somenode != None and whichorder == 'in':
TreeMap.listify_helper(somenode.left, 'in')
order_list.append(somenode.key+ '='+somenode.value)
TreeMap.listify_helper(somenode.right, 'in')
return order_list
TEST CODE:
import treemap
translator = treemap.TreeMap()
translator.add("cat", "Katze")
translator.add("bird", "Vogel")
translator.add("dog", "Hund")
translator.add("snake", "IDK")
translator.add("bear", "IDK")
translator.add("octopus", "Tintenfisch")
translator.add("horse", "Pferd")
translator.add("zebra", "IDK")
translator.print()
print("---------------------------------------------------")
print (translator.listify())
The problem is here:
def listify_helper(somenode, whichorder):
order_list = []
This function initialises its own local order_list every time it is invoked. Pass order_list as a parameter instead so that the same list is appended to by each recursive invocation.
Alternatively, append each element of the result of the recursive calls of listify_helper to order_list, although this approach could result in unneeded copying.

Question on argument in functions for deserializing the string back into tree in Python

The question that I am asking here is based on www.dailycodingproblem.com
question 3:
"Given the root to a binary tree, implement serialize(root), which serializes the tree into a string, and deserialize(s), which deserializes the string back into the tree."
The code below is one of the solutions that I found and the question I want to ask is:-
For the deserialize function in line 42, when I pass an argument such as
def deserializer(node): which is followed by return deserializer(node), the output says "node not defined error" if I input any numbers eg: 1,3,2.
However, it works when I leave out the argument part empty that is def deserializer() followed by return deserializer()?
Your help would be much appreciated!
#must create a constructor every time we create a class eg: def __init__(self)
class Node:
def __init__(self, v):
self.left = None #none = empty state
self.right = None
self.value = v
class Tree:
def __init__(self):
self.root = None
def addNode(self, node, v1):
if node == None:
self.root = Node(v1)
#argument in Node need not have to be v
else:
if v1 < node.value:
#if 2nd value less than 1st value for example?
if not node.left:
node.left = Node(v1) #will not update value
else:
#if it is node.left,update value
self.addNode(node.left, v1)
else:
if not node.right:
node.right = Node(v1)
else:
self.addNode(node.right, v1)
def deserialize(s):
values = iter(s.split(','))
def deserializer(): #why putting def deserializer(node) gives me "node undefined error"?)
val = next(values)
if val == '?':
return None
else:
node = Node(int(val))
node.left = deserializer()
node.right = deserializer()
return node
return deserializer() #why putting return deserializer(node) gives me "node undefined error"?)
if __name__ == '__main__':
# Read input, numbers separated by commas
numbers = [int(n) for n in input().split(',')]
theTree = Tree()
for number in numbers:
theTree.addNode(theTree.root, number)
s2 = serialize(deserialize(s1))
print(s2)

Append method for linked list

class _ListNode:
def __init__(self, value, next_):
self._data = value
self._next = next_
return
class List:
def __init__(self):
self._front = None
self._count = 0
return
def _linear_search(self,key):
previous = None
current = self._front
index = 0
while current is not None and key > current._data:
previous = current
current = current._next
index += 1
if current._data != key:
previous = None
current = None
index = -1
return previous, current, index
def __contains__(self, key):
_, _, i = self._linear_search(key)
return i != -1
def append(self, value):
if self._front is None:
self._front = _ListNode(value,None)
else:
self._front._next = _ListNode(value,None)
self._count += 1
l = List()
lst = [1,2,3,4]
i = 0
n = len(lst)
while i < n:
l.append(lst[i])
i += 1
print("{}".format(l.__contains(3))
To explain more, I implement the linear search method and the contains method. The contains method check if the number is in the list or not (returns true or false). Now when I need to check that #3 in the list using contains method, the answer is false!! i cant know what's the problem
Your append method does not walk down the list. It simply always appends to self._front._next if self.front is already present. Meaning the contents at the end of the append loop are the first thing you appended, the last thing you appended and nothing in between.
To correct it walk the list looking for a _next equal to None and append there.
def append(self, value):
if self._front is None:
self._front = _ListNode(value, None)
else:
n = self._front
while n._next is not None:
n = n._next
n._next = _ListNode(value, None)
self._count += 1
You could also define a _str__ method to print the content of the List
e.g.
def __str__(self):
res = []
n = self._front
while n is not None:
res.append(str(n._data))
n = n._next
return ', '.join(res)
This is not a particularly efficient implementation as it builds an intermediate builtin list object.
You also don't need those bare return statements in your methods. You can remove those.

Python Binary Tree print nodes with two exactly two children

How do I make a function that returns the number of nodes in a tree that have two children?
My class code is as follows:
class RefBinaryTree:
def __init__(self, data, left=None, right=None):
self.key = data
self.left = left
self.right = right
def insert_left(self, value):
self.left = RefBinaryTree(value, left=self.left)
def insert_right(self, value):
self.right = RefBinaryTree(value, right=self.right)
def get_left_subtree(self):
return self.left
def get_right_subtree(self):
return self.right
def set_value(self, new_value):
self.key = new_value
def get_value(self):
return self.key
def create_string(self, indent):
string = str(self.key) + '---+'
if self.left:
string += '\n(l)' + indent + self.left.create_string(indent + ' ')
if self.right:
string += '\n(r)' + indent + self.right.create_string(indent + ' ')
return string
def __str__(self):
return self.create_string(' ')
I'm guessing it would be best to use recursion. Any hints or helpful links would be awesome. Thanks.
It's really quite simple to count two-child nodes recursively. If you return a number with each function call (zero as the base case) you can simply add 1 every time you find a two-child node:
def findDoubleNodes(tree):
if tree == None or (tree.left == None and tree.right == None):
# base case
return 0
elif tree.left <> None and tree.right <> None:
# both have children, so add one to our total and go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right) + 1
else:
# only one child, so only go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right)
Inputting a RefBinaryTree returns the number of nodes with two children. An example:
x = RefBinaryTree(1)
x.insert_left(5)
x.left.insert_left(6)
x.left.insert_right(7)
x.left.right.insert_left(8)
x.left.right.insert_right(9)
x.left.right.right.insert_right(10)
The (lazily) created tree looks like this:
1
/
5
/ \
6 7
/ \
8 9
\
10
And findDoubleNodes(x) returns 2, as only two nodes (5 and 7) have two children.
Additionally, adding a left child to node 9 (x.left.right.right.insert_left(11)) has the expected result, returning 3.
This should do:
def countNodes(tree):
if tree is None:
return 0
left = tree.get_left_subtree()
rght = tree.get_right_subtree()
return (0 if left is None or rght is None else 1) \
+ countNodes(left) + countNodes(rght)

Categories