How recursion keeps the function values in python? - python

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?

Related

Binary Search Tree Delete method Deletes Whole Subtree

I have been learning data structures and have been trying to implement the delete method in my BST class. What I have noticed is that instead of deleting one node it will delete the whole subtree that the node is a part of.
class BST:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def addchild(self, child):
if child == self.data:
return
if child < self.data:
if self.left:
self.left.addchild(child)
else:
self.left = BST(child)
else:
if self.right:
self.right.addchild(child)
else:
self.right = BST(child)
def search(self, data):
if self.data == data:
return True
if data < self.data:
if self.left:
return self.left.search(data)
else:
return False
else:
if self.right:
return self.right.search(data)
else:
return False
def iot(self):
vals = []
if self.left:
vals += self.left.iot()
vals.append(self.data)
if self.right:
vals += self.right.iot()
return vals
def findmax(self):
if self.right:
return self.right.findmax()
else:
return self.data
def findmin(self):
if self.left:
return self.left.findmin()
else:
return self.data
def delete(self, data):
if data < self.data:
if self.left:
self.left = self.left.delete(data)
elif data > self.data:
if self.right:
self.right = self.right.delete(data)
else:
if self.left is None and self.right is None:
return None
elif self.left is None:
return self.right
elif self.right is None:
return self.left
minval = self.right.findmin()
self.data = minval
self.right = self.right.delete(minval)
def buildtree(arr):
r = BST(arr[0])
for i in range(1, len(arr)):
r.addchild(arr[i])
return r
So when input with these values and deleting the number 1 from the tree it will print this
bst = buildtree([34, 7, 8, 1, 3, 4, 5, 10, 65, 98, 100, 203])
print(bst.iot())
bst.delete(1)
print(bst.iot())
1st print statement output: [1, 3, 4, 5, 7, 8, 10, 34, 65, 98, 100, 203]
2nd print statement output: [34, 65, 98, 100, 203]
When I debug in Pycharm, before leaving the delete method and after all steps are executed the tree will show that the left subtree is intact and everything is how it should be. However once I leave the method the left subtree becomes None.
Any help would be appreciated.
Here's a strong hint:
You are missing a return statement on final block of you code:
minval = self.right.findmin()
self.data = minval
self.right = self.right.delete(minval)
A debug answer:
In your example 1 is going to be the leftmost leaf in the tree.
So look what’s happening:
When you start the method delete you say that
if data < self.data:
if self.left:
self.left = self.left.delete(data)
So basically the left son of the root will hold whatever comes at the end of the process. If you will follow the left branch until you get 1, you will see that you get the the case of
else:
if self.left is None and self.right is None:
return None
This means that the root left son is None, and this explains your bug

Creating a Binary Search Tree using Binary Search Tree class Implementation

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

binary search tree missing 1 required positional argument:

Trying to construct the Binary search tree here
class Node:
# Constructor to create a new node
def __init__(self, data):
self.data = data
self.left = None
self.right = None
class BSTree():
def __init__(self, rootdata):
self.root = Node(rootdata)
def insert(self, data, cur_node):
if data < cur_node.data:
if cur_node.left == None:
cur_node.left = Node(data)
else:
self.insert(data, cur_node.left)
elif data > cur_node.data:
if cur_node.right == None:
cur_node.right = Node(data)
else:
self.insert(data, cur_node.right)
else:
print("Duplicate value!")
def find(self, data, cur_node):
if data < cur_node.data and cur_node.left:
return self.find(data, cur_node.left)
elif data > cur_node.data and cur_node.right:
return self.find(data, cur_node.right)
if data == cur_node.data:
return True
return False
def PreOder(self,root):
if root == None:
pass
else:
print(root.data)
self.PreOrder(root.left)
self.PreOrder(root.right)
a = BSTree()
a.insert(3)
a.insert(4)
a.insert(7)
a.insert(34)
a.insert(24)
a.insert(2)
a.insert(49)
print(a.find(3))
print(a.PreOrder(3))
I am getting an error message: init() missing 1 required positional argument: 'rootdata'
How to fix and print the binary search tree?
Also, what I have up there is just the random number I am try to construct the binary tree out from the list I have
mylist = [1,3,2,4,12,14,23,43,23,44,34,43]
Here is a working and properly formatted update of your code. Not sure what it's doing exactly, but it should give you some clues to solve your task. Maybe you could use an IDE like Visual Studio Code or Pycharm to help you out with python specific stuff.
class Node:
# Constructor to create a new node
def __init__(self, data):
self.data = data
self.left = None
self.right = None
class BSTree:
def __init__(self, rootdata):
self.root = Node(rootdata)
def insert(self, data, cur_node):
if data < cur_node.data:
if cur_node.left is None:
cur_node.left = Node(data)
else:
self.insert(data, cur_node.left)
elif data > cur_node.data:
if cur_node.right is None:
cur_node.right = Node(data)
else:
self.insert(data, cur_node.right)
else:
print("Duplicate value!")
def find(self, data, cur_node):
if data < cur_node.data and cur_node.left:
return self.find(data, cur_node.left)
elif data > cur_node.data and cur_node.right:
return self.find(data, cur_node.right)
if data == cur_node.data:
return True
return False
def pre_order(self, root):
if root is None:
pass
else:
print(root.data)
self.pre_order(root.left)
self.pre_order(root.right)
a = BSTree(3)
a.insert(4, a.root)
a.insert(7, a.root)
a.insert(34, a.root)
a.insert(24, a.root)
a.insert(2, a.root)
a.insert(49, a.root)
print(a.find(3, a.root),)
print(a.pre_order(a.root))
Some issues:
Your BSTree constructor does not create an emtpy tree, but a tree with already one node, and so it expects the data for that node as argument. This design is not good, because it does not support the concept of an empty tree. So the constructor should change and just set self.root to None.
insert takes also more arguments than expected: it does so because it uses that argument for implementing recursion. There are many ways to make it work, but I prefer that the recursive call is made in OOP-style, i.e. the recursive insert method should be placed in the Node class, and act on self instead of on an extra argument. The insert method on the BSTree class can then just be a wrapper around that method, where it gets called on the root node.
A similar issue occurs with find. The solution can be the same as for insert.
And again, the issue occurs also with preOrder: it takes a node as argument. Same solution as discussed above. I would also avoid printing in a method. Instead yield the values.
So here is how that would look:
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def insert(self, data):
if data < self.data:
if not self.left:
self.left = Node(data)
else:
self.left.insert(data)
elif data > self.data:
if not self.right:
self.right = Node(data)
else:
self.right.insert(data)
else:
print("Duplicate value!")
def find(self, data):
if data < self.data and self.left:
return self.left.find(data)
elif data > self.data and self.right:
return self.right.find(data)
return data == self.data
def preOrder(self):
yield self.data
if self.left:
yield from self.left.preOrder()
if self.right:
yield from self.right.preOrder()
class BSTree():
def __init__(self):
self.root = None
def insert(self, data):
if not self.root:
self.root = Node(data)
else:
self.root.insert(data)
def find(self, data):
return self.root.find(data) if self.root else False
def preOrder(self):
if self.root:
yield from self.root.preOrder()
a = BSTree()
a.insert(3)
a.insert(4)
a.insert(7)
a.insert(34)
a.insert(24)
a.insert(2)
a.insert(49)
print(a.find(3))
print(*a.preOrder()) # With * we splash every yielded value as argument to print().

How to implement AVL tree rotation?

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

How to get the length of a tree in python binary search

class Node:
def __init__(self,data=None):
self.data=data
self.left_child=None
self.right_child=None
self.parent=None
self.root = None
class BinarySearchTree:
def __init__(self):
self.root=None
def add(self, data):
if self.root == None:
self.root = Node(data)
else:
self.add_helper(data, self.root)
def add_helper(self, data, cur_node):
if data < cur_node.data:
if cur_node.left_child == None:
cur_node.left_child = Node(data)
cur_node.left_child.parent = cur_node # set parent
else:
self.add_helper(data, cur_node.left_child)
elif data > cur_node.data:
if cur_node.right_child == None:
cur_node.right_child = Node(data)
cur_node.right_child.parent = cur_node # set parent
else:
self.add_helper(data, cur_node.right_child)
else:
print("data already in tree!")
def __len__(self):
if self.root is None:
return 0
else:
return (self.__len__(self.left_child) + 1 +self. __len__(self.right_child))
So i am trying to return the length of the binary search tree list, so i tried using the len method for my binary search tree class. However, this is not working correctly. I need it to be able to not take in any parameters, and just return an integer for the length of the binary search tree list. What am i missing and what am i doing wrong here?
You will need a helper function that takes a Node argument. Then do the recursion on the left and right of the node.
def __len__(self):
return self.tree_len(self.root)
def tree_len(self, node):
if node is None:
return 0
else:
return 1 + max(self.tree_len(node.right_child), self.tree_len(node.left_child))

Categories