I am implementing a BST and everything works, even the deletion with two children.The only bug is in the deletion of a leaf which seems such a trivial task.
There seem to be still a reference to the leaf node but I can’t get on top of this issue.Putting node = None doesn’t remove the entire Node.I have also tried del node
without any luck.If you could spot the problem it would be nice.
import random
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
self.parent = None
self.left_child = None
self.right_child = None
self.level = None
class Tree:
def __init__(self):
self.root = None
self.size = 0
self.height = 0
def _insertion(self, root, data):
new_node = Node(data)
if root.data < data:
if root.right:
return self._insertion(root.right, data)
root.right = new_node
root.right.parent = root
root.right_child = root.right
return
if root.data > data:
if root.left:
return self._insertion(root.left, data)
root.left = new_node
root.left.parent = root
root.left_child = root.left
return
def insertion(self, data):
new_node = Node(data)
if not self.root:
self.root = new_node
return
return self._insertion(self.root, data)
def _get_height(self, root):
if not root:
return -1
left_height = self._get_height(root.left)
right_height = self._get_height(root.right)
return 1 + max(left_height, right_height)
def get_height(self):
if not self.root:
return 0
return self._get_height(self.root)
def fill_random(self, num_nodes):
for i in range(num_nodes):
random_num = int(random.random()*100)
self.insertion(random_num)
def _inorder(self, root):
if root:
self._inorder(root.left)
print(root.data)
self._inorder(root.right)
def inorder(self):
root = self.root
return self._inorder(root)
def get_max(self, node):
while node.right:
node = node.right
return node.data
def search(self, data):
root = self.root
while root.right or root.left:
if root.data < data:
root = root.right
if root.data > data:
root = root.right
if root.data == data:
return root
return None
def _delete_node(self, root, data):
if root:
if root.data < data:
return self._delete_node(root.right, data)
if root.data > data:
return self._delete_node(root.left, data)
if root.data == data:
if not root.left and not root.right:
root = None
return
if not root.left and root.right:
root = root.right
root.right = None
return
if not root.right and root.left:
root = root.left
root.left = None
return
if root.right and root.left:
value = self.get_max(root)
print(f"This is the value: {value}")
root.data = value
self._delete_node(root.right, value)
def delete_node(self, data):
if not self.root:
return None
return self._delete_node(self.root, data)
if __name__ == '__main__':
my_tree = Tree()
my_tree.insertion(33)
my_tree.insertion(36)
my_tree.insertion(25)
my_tree.insertion(20)
my_tree.insertion(27)
my_tree.insertion(35)
my_tree.insertion(39)
my_tree.delete_node(33)
my_tree.inorder()
my_tree.search(35)
In your _delete_node function, it doesn't help to set root to None. That only affects the variable, but does not bring any change to the tree.
A common "trick" is to return root to the caller -- who is then responsible to assign that returned value back to where it is referenced in the tree. For instance, if the recursive call was made with root.left as argument, and the recursive call wants to delete that node, it will return None, and the caller must then assign (whatever it gets back) to root.left. So now the tree is mutated as intended.
Here are your delete_node functions adapted along that principle:
def _delete_node(self, root, data):
if root:
if root.data < data:
root.right = self._delete_node(root.right, data)
elif root.data > data:
root.left = self._delete_node(root.left, data)
elif not root.left:
return root.right
elif not root.right:
return root.left
else:
value = self.get_max(root)
print(f"This is the value: {value}")
root.data = value
root.right = self._delete_node(root.right, value)
return root
def delete_node(self, data):
if self.root:
self.root = self._delete_node(self.root, data)
Another remark
Your search function has some issues:
The while condition should check whether root is None before accessing any of its attributes.
The if blocks should be tied together with elif, otherwise the execution may fall into a second if block which is not the intension.
Here is the corrected version:
def search(self, data):
root = self.root
while root:
if root.data < data:
root = root.right
elif root.data > data:
root = root.left
else:
return root
return None
Little improvement
To help debug this code, I altered the inorder method with a little change, so that the tree is printed with indentations:
def _inorder(self, root, indent=""):
if root:
self._inorder(root.left, indent+" ")
print(indent + str(root.data))
self._inorder(root.right, indent+" ")
This helps to quickly see the structure of the tree. You might even want to switch left and right, so that it looks like a rotated tree.
This block of code doesn't do anything:
if root.data == data:
if not root.left and not root.right:
root = None
return
because root is a local variable; reassigning it to None doesn't affect the tree.
What you need to do is change the parent node's right or left pointer. I'd suggest doing this before you recurse, but you could also solve it by passing the parent node into this function.
Related
class node :
def __init__(self, data, left= None, right = None):
self.data = data
self.left = left
self.right = right
class bst:
def __init__(self):
self.head = None
def insertNode(self, data):
newnode = node(data)
if self.head == None:
self.head = newnode
else:
current = self.head
while current is not None:
if data > current.data:
current = current.right
else:
current = current.left
current = newnode
bst = bst()
bst.insertNode(5)
bst.insertNode(10)
bst.insertNode(2)
current = bst.head
print(current.data)
print(current.right)
I have written a code for the best where the head is always pointing to the root node and there is a while loop which is to find the path to insert the new element to the tree but it is not woking ?? why
In this situation, it may be easier to understand and complete the problem using a recursive approach. We can use recursion to explore the tree, traversing all nodes. Try something like this:
def insert(root, data):
if root is None:
return Node(data)
else:
if root.data == data:
return root
elif root.data < data:
root.right = insert(root.right, data)
else:
root.left = insert(root.left, data)
return root
In your while loop before assigning current to either the Left or Right Tree
check whether the Left or Right Child is None, then add a stop condition (break) if the child is None and you have assigned the newnode to it as in the code below
class node :
def __init__(self, data, left= None, right = None):
self.data = data
self.left = left
self.right = right
class bst:
def __init__(self):
self.head = None
def insertNode(self, data):
newnode = node(data)
if self.head == None:
self.head = newnode
else:
current = self.head
while current is not None:
if data > current.data:
if current.right!=None:
current = current.right
else:
current.right=newnode
break
else:
if current.left!=None:
current = current.left
else:
current.left=newnode
break
current = newnode
bst = bst()
bst.insertNode(5)
bst.insertNode(10)
bst.insertNode(2)
current = bst.head
print(current.data)
print(current.right)
The problem is that you are separating the tree and the search into different classes, also, you initialize left and right as None and they will never be giving a value, so your loop will always end setting current as None.
Here is an example of what you are trying to make.
class Tree(object):
def __init__(self, entry, left=None, right=None):
self.entry = entry
self.left = left
self.right = right
def insert(self, item, tree):
if (item < tree.entry):
if (tree.left != None):
self.insert(item, tree.left)
else:
tree.left = Tree(item)
else:
if (tree.right != None):
self.insert(item, tree.right)
else:
tree.right = Tree(item)
As you can see I added a check for right and left in case they are None, in which case they are given the item's value.
The problem lies in the current = newnode. You should not directly assign newnode to current, which will only rebind current to it.
def insert(self, data):
current = self.head
parent = None
while current is not None:
if data == current.data:
return
parent = current
current = current.left if data < current.data else current.right
newnode = node(data)
if parent is None:
self.head = newnode
elif data < parent.data:
parent.left = newnode
else:
parent.right = newnode
Your mistakes are similar to the following:
>>> lst = [None, None]
>>> current = lst[0]
>>> print(current)
None
>>> current = 1
>>> lst # not change
[None, None]
current simply points to the first item in the list. If you re assign value, you will only make current point to other value, which will not modify the contents of the list.
first post here. I am supposed to build a BST (which I have done) and create a deleteNode function. I keep trying to run the function but it is unfortunately not working.
#deleteFunction#
def deleteNode(self, data):
if self is None: ##none is left and right val
return self
if data < self.data: #if input is less than current
self.left = self.deleteNode(self.left, data) #go to the left node
elif (data > self.data): #if input is higher, go to right node
self.right = self.deleteNode(self.right, data)
else:
if self.left is None:
temp = self.right #if left is none then assign temp to right
self.left = None
return temp
elif self.right is None: #if right is none, assign temp to left
temp = self.left
self.left = None
return temp
temp = findMinNode(self.right) ##node with two children, get the smallest right subtree
self.data = temp.data ##copy the right small subtree
self.right = deleteNode(self.right, temp.data) #delete smallest right subtree
return self
##Execution code:##
print("Maximum node in BT: \n", dTree.findMaxNode())
print("Minimum node in BT: \n",dTree.findMinNode())
print("Post Order: \n", dTree.postOrderTrav())
print("Pre Order: \n", dTree.preOrderTrav())
print("In Order: \n", dTree.inOrderTrav())
dTree.deleteNode(4)
print("deletion of one node: ")
print (dTree.inOrderTrav())
I keep receiving the following error:
line 262, in <module> dTree.deleteNode(4)
File "C:/Users", line 215, in deleteNode self.right = self.deleteNode(self.right, data)
TypeError: deleteNode() takes 2 positional arguments but 3 were given
200
This is my favorite version of deleting a node in BST - use deleteNode function, with root being the root node and key being the node value that you want to delete.
class DeletingNodeBST:
def successor(self, root):
root = root.right
while root.left:
root = root.left
return root.val
def predecessor(self, root):
root = root.left
while root.right:
root = root.right
return root.val
def deleteNode(self, root, key):
if not root:
return None
if key > root.val:
root.right = self.deleteNode(root.right, key)
elif key < root.val:
root.left = self.deleteNode(root.left, key)
else:
if not (root.left or root.right):
root = None
elif root.right:
root.val = self.successor(root)
root.right = self.deleteNode(root.right, root.val)
else:
root.val = self.predecessor(root)
root.left = self.deleteNode(root.left, root.val)
return root
Note that root is the root node, which can be created with:
class Node:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
can someone help me out with this? I implemented a binary search tree data structure in python and I wrote a BST_height() function to calculate the height of the tree. But when I ran my code, It gave me an error saying 'self is not defined'. I know why the error is showing up but can you suggest some other way to run the BST_height function with the root node
class Node:
def __init__(self, data=None):
self.data = data
self.left = None
self.right = None
class BinarySearchTree:
def __init__(self):
self.root = Node()
def display(self):
print('''
{}
/ \\
{} {}
/ \\ / \\
{} {} {} {}
BINARY TREE
'''.format(tree.root.data, tree.root.left.data, tree.root.right.data, tree.root.left.left.data, tree.root.right.right.data, tree.root.left.right.data, tree.root.right.left.data))
def checkRoot(self):
if self.root.data != None:
return 'Root node exists'
else:
return 'Root node doesn\'t exists'
def insert(self, data):
newNode = Node(data)
if self.root.data == None:
# creating the root node
self.root = newNode
else:
self.insertNode(data, self.root)
def insertNode(self, data, curNode):
if data < curNode.data:
if curNode.left == None:
curNode.left = Node(data)
else:
self.insertNode(data, curNode.left)
elif data > curNode.data:
if curNode.right == None:
curNode.right = Node(data)
else:
self.insertNode(data, curNode.right)
else:
print('The value already exists ha ha')# funny
def BST_height(self, node):
if node == None:
return -1
leftHeight = height(node.left)
rightHeight = height(node.right)
return max(leftHeight, rightHeight) + 1
tree = BinarySearchTree()
tree.insert(30)# root node
tree.insert(24)
tree.insert(45)
tree.insert(90)
tree.insert(18)
tree.insert(28)
tree.insert(40)
tree.display()
# getting an error here
# I know self.root can\'t be used outside the class but can you suggest some other way tree.BST_height(self.root)
There are 2 problems with the code:
Self is something you use inside of class functions. Outside you can simply use the object variable. like so:
tree.BST_height(tree.root)
BST_height function has a small error in it. It calls height instead of self.BST_height:
def BST_height(self, node):
if node == None:
return -1
leftHeight = self.BST_height(node.left)
rightHeight = self.BST_height(node.right)
return max(leftHeight, rightHeight) + 1
You might want to read this for clarity on the whole self topic.
I am implementing BST in python and having some trouble on insertion function.
class Node:
def __init__(self, val):
self.data = val
self.Leftchild = self.Rightchild = None
class Tree:
def __init__(self):
self.root = None
def insert(self, val):
if self.root is None:
self.root = Node(val)
return self.root
else:
if self.root.data <= val:
self.root.Rightchild = self.insert(self.root.Rightchild, val)
else:
self.root.Leftchild = self.insert(self.root.Leftchild, val)
return self.root
if __name__ == '__main__':
tree = Tree()
for i in range(10):
tree.insert(random.randint(0,100))
I got TypeError on recursive.
TypeError: insert() takes 2 positional arguments but 3 were given
Isn't self.root.Rightchild or self.root.Leftchild considered same as self?
If my thought is wrong, how can I implement recursive insertion function in this case?
Thanks in advance!
You should have insert take another argument, root, and do your operations on that. You'll need to modify the recursive logic too. Have insert work exclusively with the Node data.
You should handle cases where an item already exists. You don't want duplicates put into the tree.
In the recursive case, you are calling insert on the wrong object.
Also, the two are not the same. self refers to the current Tree object, and self.root refers to the current Tree's Node object, and so on.
This is how you'd modify your function:
def insert(self, root, val):
if root is None:
return Node(val)
else:
if root.data <= val:
root.Rightchild = self.insert(root.Rightchild, val)
else:
root.Leftchild = self.insert(root.Leftchild, val)
return root
Try this one if you need class and its instance
import random
class Node:
def __init__(self, val):
self.data = val
self.Leftchild = self.Rightchild = None
class Tree:
def insert(self,root, val):
if root is None:
root = Node(val)
return root
else:
if root.data <= val:
root.Rightchild = self.insert(root.Rightchild, val)
else:
root.Leftchild = self.insert(root.Leftchild, val)
return root
def inorder(self, root):
if root:
self.inorder(root.Leftchild)
print root.data,
self.inorder(root.Rightchild)
if __name__ == '__main__':
tree = Tree()
root = None
for i in range(10):
root = tree.insert(root, random.randint(0,100))
tree.inorder(root)
Try this..
class Node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
def inorder(root):
if root:
inorder(root.left)
arr.append(root.data)
print root.data,
inorder(root.right)
def insert(root, data):
node = Node(data)
if root is None:
root = node
elif root.data >= data:
if root.left is None:
root.left = node
else:
insert(root.left, data)
else:
if root.right is None:
root.right = node
else:
insert(root.right, data)
if __name__ == '__main__':
root = Node(50)
insert(root, 30)
insert(root, 20)
insert(root, 40)
insert(root, 70)
insert(root, 60)
insert(root, 80)
inorder(root)
This is my code for my binary search tree implementation:
class Tree(Node):
def __init__(self, root):
self.root = root
def find(self, data, root):
if root == None:
return False
wanderer = root
if data == wanderer.data:
return True
elif data > wanderer.data:
return self.find(data, root.right)
else:
return self.find(data, root.left)
return False
def insert(self, data, root):
if root == None:
root = Node(data)
return True
if data > root.data:
return self.insert(data, root.right)
elif data < root.data:
return self.insert(data, root.left)
else:
return False
My insert method doesn't seem to be working and I can't figure out what is wrong with it. Please help.
root = Node(data) does not assign the new node back to the caller argument root.right or root.left.
See https://nedbatchelder.com/text/names1.html for more details.