I have coded an AVL Tree and my logic for the rotations is correct but I am still not able to get it working properly. For rotations on the root node my rotations work properly but if the rotation is further down the tree, the parent node does not point to the new node that has been rotated into place and continues to point to the node that was in place before the rotation. I am pretty sure the issues lies with my insert method but I am not sure how to get the parent node to point to the new node when a rotation occurs. I know you can add a parent variable to fix this but I am wondering if there is a way to do it without that.
For example
10 10 10
/ \ / \ instead of / \
8 12 Rotates to -> 8 12 6 12
/ \ \ / \ \
6 14 14 4 8 14
/ 4 and 6 are lost
4
class AVL():
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.height = 0
self.balf = 0
def getData(self):
return self.data
def getHeight(self):
return self.height
def heightCalc(self,node):
if node is None:
return -1
else:
return max(self.heightCalc(node.left), self.heightCalc(node.right)) + 1
def getBalanceFactor(self):
return self.balf
def balCheck(self, node):
if node is None:
return -1
else:
return self.heightCalc(node.left) - self.heightCalc(node.right)
def insert(self, data):
if data is not None:
if self.data is None:
self.data = data
else:
if data < self.data:
if self.left is None:
self.left = AVL(data)
else:
self.left.insert(data)
elif data >= self.data:
if self.right is None:
self.right = AVL(data)
else:
self.right.insert(data)
self.height=self.heightCalc(self)
self.balf = self.balCheck(self)
if self.balf > 1:
if self.left.getBalanceFactor() < 0:
self.left = self.left.leftRotate()
return self.rightRotate()
else:
return self.rightRotate()
elif self.balf < -1:
if self.right.getBalanceFactor() > 0:
self.right = self.right.rightRotate()
return self.leftRotate()
else:
return self.leftRotate()
return self
def leftRotate(self):
temp = self.right
temp2 = self.right.left
self.right.left = self
self.right = temp2
self.height = self.heightCalc(self)
temp.height = self.heightCalc(temp)
self.balf = self.balCheck(self)
temp.balf = self.balCheck(temp)
return temp
def rightRotate(self):
tmp = self.left
tmp1 = self.left.right
self.left.right = self
self.left = tmp1
self.height = self.heightCalc(self)
tmp.height = self.heightCalc(tmp)
self.balf = self.balCheck(self)
tmp.balf = self.balCheck(tmp)
return tmp
#This example works properly
test = AVL(10)
test= test.insert(12)
test = test.insert(8)
print(test.data) #outputs 8
print(test.left.data) #outputs 7
print(test.right.data) #outputs 10
#In this case the rotation occurs but the parent node does not update its left child to the new node and still points to 8
test2 = AVL(10)
test2 = test2.insert(12)
test2 = test2.insert(8)
test2 = test2.insert(14)
test2 = test2.insert(6)
test2 = test2.insert(4)
print(test2.data)#outputs 10
print(test2.left.data)#outputs 8 but should be 6
#4 and 6 can no longer be accessed because they are lost
In your code, the insert method returns the new root of the subtree, after the insertion has been done and any needed rotations have happened. Your issue is that you're not using that return value when you recursively call insert on one of your child nodes.
if data < self.data:
if self.left is None:
self.left = MyAVL(data)
else:
self.left = self.left.insert(data) # update self.left here
elif data >= self.data:
if self.right is None:
self.right = MyAVL(data)
else:
self.right = self.right.insert(data) # and self.right here
Related
I have defined the BinarySearchTree class below:
class BinarySearchTree:
def __init__(self, data, left=None, right=None):
self.data = data
self.left = left
self.right = right
def insert(self, new_data):
if new_data == self.data:
return
elif new_data < self.data:
if self.left == None:
self.left = BinarySearchTree(new_data)
else:
self.left.insert(new_data)
else:
if self.right == None:
self.right = BinarySearchTree(new_data)
else:
self.right.insert(new_data)
def search(self, find_data):
if self.data == find_data:
return True
elif find_data < self.data and self.left != None:
return self.left.search(find_data)
elif find_data > self.data and self.right != None:
return self.right.search(find_data)
else:
return False
def get_data(self):
return self.data
def set_data(self, new_data):
self.data = new_data
def get_left(self):
return self.left
def get_right(self):
return self.right
Using this class implementation now I need to create a binary tree as shown in the vertical representation:
>>> bst = create_bst()
print_tree(bst, 0)
27
(L) 14
(L) 10
(R) 19
(R) 35
(L) 31
(R) 42
With my code here:
def create_bst():
root = BinarySearchTree(27)
root.insert(14)
root.insert(10)
root.get_left().insert(19)
root.get_left().insert(35)
root.get_left().get_left().insert(31)
root.get_left().get_right().insert(42)
return root
This is the representation I'm getting:
27
(L) 14
(L) 10
(R) 31
(R) 19
(R) 35
(R) 42
It seems to be working fine so far. Just put all the elements directly into root without using get_left, get_right (since you are inserting them into the wrong node).
def create_bst():
root = BinarySearchTree(27)
root.insert(14)
root.insert(10)
root.insert(19)
root.insert(35)
root.insert(31)
root.insert(42)
return root
I am on the way of learning data structures and facing a problem which is related with python and recursion.
I have a function (below - called update_BST) and in one version of the code doesn't keep the correct value from recursion.
I provide the two version of the code snippet.
Thanks
"""
Given a Binary Search Tree (BST),
modify it so that all greater values in
the given BST are added to every node.
For example, consider the following BST.
50
/ \\
30 70
/ \ / \\
20 40 60 80
The above tree should be modified to following
260
/ \\
330 150
/ \ / \\
350 300 210 80
"""
class Node:
def __init__(self, data):
self.data = data
self.right = None
self.left = None
def insert(self, data):
if self.data == data:
return False
elif self.data > data:
if self.left:
self.left.insert(data)
else:
self.left = Node(data)
else:
if self.right:
self.right.insert(data)
else:
self.right = Node(data)
def in_order(self):
if self:
if self.left:
self.left.in_order()
print(self.data)
if self.right:
self.right.in_order()
class BST:
def __init__(self):
self.root = None
def insert(self, data):
if self.root:
self.root.insert(data)
else:
self.root = Node(data)
return True
def in_order(self):
if self.root is not None:
self.root.in_order()
bst = BST()
arr = [50, 30, 20, 40, 70, 60, 80]
for i in arr:
bst.insert(i)
def update_BST(node, temp):
if node == None:
return
update_BST(node.right, temp)
temp[0] = temp[0] + node.data
node.data = temp[0]
update_BST(node.left, temp)
update_BST(bst.root, [0])
bst.in_order()
This codes works as it suppose to work. It gives back the right values.
BUT, I don't understand why it is not working if I use -- 0 -- instead of the -- [0] -- and of course modifying the reamining code like:
def update_BST(node, temp):
if node == None:
return
update_BST(node.right, temp)
temp = temp + node.data
node.data = temp
update_BST(node.left, temp)
update_BST(bst.root, 0)
So the question is why I need to use the [0] - why the simple integer 0 is not working?
I am trying to find the height of a BST but it is giving error like 'NoneType' object has no attribute 'height'. I can't figure out the error.
class BST:
def __init__(self,val):
self.left = None
self.right = None
self.root = val
def insert(self,data):
if self.root == None:
self.root = BST(data)
elif data > self.root:
if self.right == None:
self.right = BST(data)
else:
self.right.insert(data)
elif data < self.root:
if self.left == None:
self.left = BST(data)
else:
self.left.insert(data)
def inorder(self):
if self.left != None:
self.left.inorder()
print(self.root)
if self.right != None:
self.right.inorder()
def height(self):
if self.root == None:
return 0
else:
return 1 + max(self.left.height(), self.right.height())
t = BST(4)
t.insert(1)
t.insert(7)
t.insert(3)
t.insert(6)
t.insert(2)
t.insert(5)
t.inorder()
print(t.height())
You need to change your init method to be this:
def __init__(self,val):
self.left = None
self.right = None
self.root = val
self.rheight = 0
self.lheight = 0
And your height method to be this:
def height(self):
if self.root == None:
return 0
else:
if hasattr(self.left, 'height'):
self.lheight = self.left.height()
if hasattr(self.right, 'height'):
self.rheight = self.right.height()
return 1 + max(self.lheight, self.rheight)
The reason this needs to change is, you are calling height all the way down your tree, thus getting all the way to None on the right and left sides, all the way at the bottom of the tree. So what this does is check if self.right and self.left have the attribute of height. They won’t if the type is None, so when both are None, we return all the way back out.
When you get to this line
return 1 + max(self.left.height(), self.right.height())
Then at some point, self.left becomes not defined (though not at the very start). You can check this by adding print(self.left) just before that statement, and you will see None outputted just before the error message.
This means that while self.root is defined, your base case needs to includes self.left (and possibly self.right), so that at no point are any of those None.
Replace this line:
return 1 + max(self.left.height(), self.right.height())
with
if hasattr(self.left, 'height'):
left_height = self.left.height()
if hasattr(self.right, 'height'):
right_height = self.right.height()
return 1 + max(left_height, right_height)
I have got adding method in my AVL tree implementation.
class Node:
def __init__(self, data, left = None, right = None, height = -1):
self.data = data
self.left = left
self.right = right
self.height = height
def addNode(self, data):
if self.data > data:
'''Adding to left subtree'''
if self.left is None:
self.left = Node(data)
else:
self.left.addNode(data)
if ( abs(self.right.height - self.left.height) == 2):
''' Then we need to balance a subtree'''
print("Rebalancing after inserting", data)
if (data < self.left.data):
self.rotateLeft()
else:
self.doubleRotateLeft()
elif self.data < data:
'''Adding to right subtree'''
if self.right is None:
self.right = Node(data)
else:
self.right.addNode(data)
if ( abs(self.right.height - self.left.height) == 2):
''' Then we need to balance a subtree'''
print("Rebalancing after inserting", data)
if (data < self.right.data):
self.rotateRight()
else:
self.doubleRotateRight()
But when I try to do
self.right.height
and self hasn't got right object then it will not return height, even default value is -1. How can I fix it? I am trying to make as less duplicated code in this method as possible
If self.right is set to None you can't use self.right.height, no. Don't use None if that expression must work. Use a sentinel that defines that attribute, instead.
A sentinel can be just a custom class that has no value and no child nodes. You can make that a singleton just like None:
class Sentinel(object):
value = left = right = None
height = -1
sentinel = Sentinel()
class Node:
def __init__(self, data, left=sentinel, right=sentinel, height=-1):
# ...
then test for is sentinel instead of is None in your code. By using sentinel as the default value for the left and right keyword arguments, self.left.height and self.right.height will always work (provided self is an instance of Node).
Don't forget to increment height each time you create a new node though.
You can simplify your addNone() method a little by using local references:
def addNode(self, data):
if self.data == data: return
left = self.data > data
testnode = self.left if left else self.right
if testnode is sentinel:
node = Node(data)
setattr(self, 'left' if left else 'right', node)
else:
testnode.addNode(data)
if abs(self.right.height - self.left.height) == 2:
if data < testnode.data:
rotation = self.rotateLeft if left else self.rotateRight
else:
rotation = self.doubleRotateLeft if left else self.doubleRotateRight
rotation()
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)