Binary Search Tree Delete method Deletes Whole Subtree - python

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

Related

Binary search tree - why does it not work without the "return" statement

When I want to search for a name which is in the tree, without the return statement I only get None but why?
See code comment #<----
class Tree:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def child(self, data):
if self.data == data:
return
if self.data < data:
if self.right:
self.right.child(data)
else:
self.right = Tree(data)
elif self.data > data:
if self.left:
self.left.child(data)
else:
self.left = Tree(data)
def search(self, elem):
if self.data == elem:
return True
if self.data < elem:
if self.right:
return self.right.search(elem) #<----
else:
self.right = False
elif self.data > elem:
if self.left:
return self.left.search(elem) #<----
else:
self.left = False
Without return, you ignore the result of the subtree search and fall through to the end of the method, at which point you return None implicitly.
You also need to return False when the appropriate subtree is empty, not set the subtree reference to False.
def search(self, elem):
if self.data == elem:
# Found it!
return True
elif self.data < elem:
# If it exists, it's in the right subtree
return self.right is not None and self.right.search(elem)
else:
# If it exists, it's in the left subtree
return self.left is not None and self.left.search(elem)

How to yield all elements on each depth level of BST?

I am trying to create generator which will give me values from next depth level of BST. Let's say that our tree will look like:
I would like to get result for following code like:
for level in tree:
print(level)
# 3
# (1,5)
# (2,4,7)
I was testing different variations of yield usage, but none of them gave me expected result. Here is code which I ended with:
class Node:
def __init__(self, element):
self.left = None
self.right = None
self.data = element
def __iter__(self):
yield self.data
if self.left and self.right:
yield from self.left.__iter__() and self.right.__iter__()
elif self.left:
yield from self.left.__iter__()
elif self.right:
yield from self.right.__iter__()
else:
pass
def add_element(self, element):
if self.data > element:
if self.left is None:
self.left = Node(element)
else:
self.left.add_element(element)
else:
if self.right is None:
self.right = Node(element)
else:
self.right.add_element(element)
def get_structure(self):
return (self.left.get_structure() if self.left else None, self.data, self.right.get_structure() if self.right else None)
This is given example from top:
from node import *
tree = Node(3)
tree.add_element(5)
tree.add_element(1)
tree.add_element(2)
tree.add_element(4)
tree.add_element(7)
print('Structure:',tree.get_structure()) #result: (None, 1, (None, 2, None)), 3, ((None, 4, None), 5, (None, 7, None)))
print('Iteration:', end=" ") #reslut: 3 5 7 / expected result: 3 (1,5) (2,4,7)
for level in tree:
print(level, end=" ")
Explain me please how can I fix my __iter__ method to get values from each depth level of tree?

How to fix yield usage in this BST implementation?

I would like to get values from tree as generator to iterate over them. In this case yield just gives me a single value. Function print_tree works as expected - it shows values from smallest to biggest. How to get similar result, but with catching them one by one? This is my BST implementation:
class Node:
def __init__(self, element):
self.left = None
self.right = None
self.data = element
def add_element(self, element):
if self.data > element:
if self.left is None:
self.left = Node(element)
else:
self.left.add_element(element)
else:
if self.right is None:
self.right = Node(element)
else:
self.right.add_element(element)
def print_tree(self):
if self.left: self.left.print_tree()
print(self.data, end=' ')
if self.right: self.right.print_tree()
def get_values(self):
if self.left: self.left.get_values()
yield self.data
if self.right: self.right.get_values()
def get_structure(self):
return (self.left.get_structure() if self.left else None, self.data, self.right.get_structure() if self.right else None)
Code for testing:
from node import *
x = Node(3)
x.add_element(5)
x.add_element(1)
x.add_element(7)
print(x.get_structure()) #result: ((None, 1, None), 3, (None, 5, (None, 7, None)))
x.print_tree() #result: 1 3 5 7
z = x.get_values()
print(list(z)) #result: [3]
Could you please explain me where am I making a mistake and how can I correct it?
First you yield all elements from the left subtree, then the node value and finally all elements from the right subtree
def get_values(self):
if self.left:
yield from self.left.get_values()
yield self.data
if self.right:
yield from self.right.get_values()

How recursion keeps the function values in 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?

Python binary tree

I have implemented a simple tree and I have this problem. When I try to search for a node and it exists it returns None even though it runs the print at the if statement in lookup method. It runs ok when I lookup for the root node. All the rest nodes return None. Could someone explain why ?
class Node():
def __init__(self,data):
self.right = None
self.left = None
self.data = data
def insert(self,data):
if self.data == data:
print "this item exists"
elif self.data > data:
if self.left == None:
self.left = Node(data)
else:
self.left.insert(data)
else:
if self.right == None:
self.right = Node(data)
else:
self.right.insert(data)
def print_nodes(self):
if self.left:
self.left.print_nodes()
print self.data
if self.right:
self.right.print_nodes()
def lookup(self,data):
if self.data == data:
print 'exists'
return 1
elif self.data > data:
if self.left != None:
self.left.lookup(data)
else:
return -1
elif self.data < data:
if self.right != None:
self.right.lookup(data)
else:
return -1
def delete(self,data):
if self.lookup(data)== -1:
print "doesnot exists"
else:
if (not self.left) and (not self.right):
self.data = None
root = Node(5)
#root.insert(3)
root.insert(3)
root.insert(2)
root.insert(6)
root.insert(61)
root.insert(62)
root.insert(63)
x = root.lookup(3)
print x
when the item is not exists in the root, you call to its sons lookup() functions without returning their value, so even if the code finds the data somewhere in the tree, you get None value instead of the result (1/-1)
Replace this lines:
self.left.lookup(data)
...
self.right.lookup(data)
with the lines:
return self.left.lookup(data)
...
return self.right.lookup(data)

Categories