Issue with the delete method of a Binary Search Tree in Python - python

I am trying to learn the implementation of a Binary Search Tree in Python with the help of the following link Binary Search Tree in Python
I am unable to implement the deletion method properly.
Here's my code:
class Node:
def __init__(self,data):
self.data=data
self.left=None
self.right=None
def Insert_BTreeNode(self,data):
if self.data:
if data<self.data:
if self.left is None:
self.left=Node(data)
else:
self.left.Insert_BTreeNode(data)
elif data>self.data:
if self.right is None:
self.right=Node(data)
else:
self.right.Insert_BTreeNode(data)
else:
self.data=data
def Lookup(self,data,parent=None):
if data<self.data:
if self.left is None:
return None,None
return self.left.Lookup(data,self)
elif data>self.data:
if self.right is None:
return None,None
return self.right.Lookup(data,self)
else:
print(self.data,parent.data)
def Children_count(self):
count=0
if self.left:
count+=1
if self.right:
count+=1
print(count)
def Delete(self,data):
children_count=0
node=self.Lookup(data)
parent=None
if node is not None:
children_count=node.Children_count()
if children_count==0:
if parent:
if parent.left is Node:
parent.left=None
else:
parent.right=None
del Node
else:
self.data=data
def print_treeInorder(self):
if self.left:
self.left.print_treeInorder()
print(self.data)
if self.right:
self.right.print_treeInorder()
def print_treePostorder(self):
if self.left:
self.left.print_treePostorder()
if self.right:
self.right.print_treePostorder()
print(self.data)
def print_treePreorder(self):
print(self.data)
if self.left:
self.left.print_treePreorder()
if self.right:
self.right.print_treePreorder()
root=Node(8)
root.Insert_BTreeNode(3)
root.Insert_BTreeNode(10)
root.Insert_BTreeNode(1)
root.Insert_BTreeNode(6)
root.Insert_BTreeNode(4)
root.Insert_BTreeNode(7)
root.Insert_BTreeNode(14)
root.Insert_BTreeNode(13)
root.Delete(13)
root.print_treeInorder()
This is kind of like homework so I'd appreciate if people give me solutions related to my code and not external libraries.
Also I'd appreciate if anyone could comment where the code is wrong.
Thanks in Advance.

To allow the function Delete() of the class Node to work properly when using code provided at "Blog: Binary Search Tree library in Python", it is necessary to correct the following missing and typo errors.
Step 1 - return the expected couple (node,parent) from the Lookup() function.
As commented above and described in the blog, when finding the node to
be removed, the function shall return both node and parent.
Don't try to print parent.data when parent is still equal to None.
def Lookup(self,data,parent=None):
if data<self.data:
if self.left is None:
return (None,None)
return self.left.Lookup(data,self)
elif data>self.data:
if self.right is None:
return (None,None)
return self.right.Lookup(data,self)
else:
# prevent case of parent is None
if (parent is not None):
print(self.data,parent.data)
# ADDED from blog link
return (self, parent)
Step 2 - return the number from the Children_count() function.
As commented above and described in the blog, the result of the
function Children_count() shall be returned to be taken into
account.
def Children_count(self):
count=0
if self.left:
count+=1
if self.right:
count+=1
print(count)
# ADDED from link
return (count)
Step 3 - manage the returned values from self.Lookup(data) in the Delete() function.
As commented above and described in the blog, the returned value of
Lookup() is a couple of nodes.
def Delete(self,data):
children_count=0
# INSERTED from link
node, parent = self.Lookup(data)
...
Instead of:
def Delete(self,data):
children_count=0
node =self.Lookup(data)
parent=None
...
Step 4 - Python is a case sensitive language, Node and node are not equivalent.
As commented above and described in the blog, to call a function of a
class, it is necessary to use its instance node instead of its class
name Node.
In the case if children_count==0::
if children_count==0:
print(" - The node to remove has no child.")
if parent:
# INSERTED from link
if parent.left is node:
parent.left=None
else:
parent.right=None
# INSERTED from link
del node
else:
self.data=data
Instead of:
if children_count==0:
if parent:
if parent.left is Node:
parent.left=None
else:
parent.right=None
del Node
else:
self.data=data
What else ?
Reuse the source code of blog for both elif children_count == 1: (The node to remove has 1 child.) and else: (The node to remove has 2 children.) and the function Delete() will work properly.
Test case 1 - The node to remove has no child.
root.Delete(13)
Test case 2 - The node to remove has 1 child.
root.Delete(10)
Test case 3 - The node to remove has 2 children.
root.Delete(6)
Test case 4 - The node to remove has 2 children and is the root node.
root.Delete(8)

Related

Tree Inorder Traversal Python

I'm trying to add elements to a binary tree and print them in in-order.
I'm getting an error while adding an element: AttributeError: 'NoneType' object has no attribute 'left'
Please let me know where I have to make a change Below is the code
class Node:
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def insert(self, data):
if self.data:
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.data = data
def InorderTraversal(self):
if self.data is None:
return
self.left.InorderTraversal()
print(self.data)
self.right.InorderTraversal()
if __name__ == "__main__":
root = Node(1)
root.insert(2)
root.insert(3)
root.insert(4)
root.InorderTraversal()
I am Implementing Trees First time Doesn't Have any idea
You should check if a node has left & right children before recursing on them:
class Node:
...
def InorderTraversal(self):
if self.left: # if left child exists, then recurse on it
self.left.InorderTraversal()
print(self.data)
if self.right: # if right child exists, then recurse on it
self.right.InorderTraversal()
Result:
1
2
3
4
You need the Node to be a separate class which will be used by the Tree class.
The insert and inorder_traversals are operations you do on a tree and not on a node. This is kinda the first thing you should do.
There are already pretty good resources you can look at since I might not be able to explain in that kind of detail :
https://www.geeksforgeeks.org/tree-traversals-inorder-preorder-and-postorder/
Also, you need to read up a little on how recursion and the classes work in python as I see some mistakes that can be fixed.
You're recursively printing the inorder traversal but you're doing the checks on self.data and running on self.left and self.right . self is pointing to the same data and you need to pass the node you wanna process next in the recursive function.
Also, you need to check the None condition on the Node and not on the data. Basically the node doesn't exist and that means you've to return from there
The inorder code overall idea is correct but you're working with wrong variables and basically you'll end up with incorrect output (or infinite loop)
My suggestion is to read up some more on tree on geeksforgeeks to begin with!

Why I am seeing None data after deleting the smallest or largest item from Binary Search Tree?

I have a binary search tree. I have written basic insert, delete, traversal of the full tree and find its maximum and minimum node of the tree but I have trouble with finding maximum and minimum node after deleting the minimum or the maximum node.
This function deletes a specific node:
def deleteNode(self,val):
if self.data:
if self.data > val:
self.left.deleteNode(val)
elif self.data < val:
self.right.deleteNode(val)
else:
if self.left is None:
self.data = self.right
return True
elif self.right is None:
self.data = self.left
return True
else:
dltNode = self
dltNode.data = self.data
largest = self.left.findMax()
dltNode.data = largest
dltNode.left.deleteNode(largest)
return True
This function finds the minimum node:
def findMin(self):
if self.data:
if self.left is None:
return self.data
else:
return self.left.findMin()
And this is for the maximum node:
def findMax(self):
if self.data:
if self.right is None:
return self.data
else:
return self.right.findMax()
findMin and findMax functions work fine without deleting any node.
If I ever call then after deleting the minimum and maximum node they will return None, whether they were supposed to return only integer node data. Here is the screenshot of my output:
It should print 34 instead of None.
Here is my full code
There are a few issues in deleteNode
if self.data should not be there: this would mean you cannot delete a node with value 0 from the tree. A similar problem exists in other methods (findMin, findMax, insert, ...).
self.left.deleteNode(val) is executed without checking first that self.left is not None. The same is true for self.right.deleteNode(val)
self.data = self.right assigns a Node reference to a data attribute (which is supposed to be a number).
The function sometimes returns nothing (None) or True. Because of the recursive nature, the original caller of the method will get None. This is not consistent.
The function cannot deal with deleting the root Node, as the caller will keep using the root node reference (t or t2 in your example code).
To solve these issues, you should either create a separate Tree class, or agree that deleteNode returns the root node of the tree, which could be a different root than the root on which the call was made (when the root node was deleted), or even None when that was the last node of the tree.
Here is how that would look:
def deleteNode(self,val):
if self.data > val:
if self.left:
self.left = self.left.deleteNode(val)
elif self.data < val:
if self.right:
self.right = self.right.deleteNode(val)
else:
if self.left is None:
return self.right
elif self.right is None:
return self.left
else:
largest = self.left.findMax()
self.data = largest
self.left = self.left.deleteNode(largest)
return self
To do it right, you would need to use the return value from this method, for example:
t1 = t1.deleteNode(34)
NB: In some methods you check whether self.data is None. I understand this is some special condition for indicating that the root node is not really a node, and should be considered an empty tree, but this is not a nice pattern. Instead, an empty tree should be just None, or you should define another Tree class which has a root attribute which can be None.

How do I call a method from my outer class in an inner class of my custom-made linked list?

I'm learning python and challenging myself by writing my own linked list from scratch. I'm using a tradition structure of an inner node class which holds a piece of data and a reference to the next node. Right now I'm trying to create a __repr__ method that returns a string representation of a node. The string it returns looks like this: "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
It works fine unless there's only 1 node in the list, which gave me the following error: AttributeError: 'NoneType' object has no attribute 'data'.
To get around this, I check first to see if there's only one node in the list, and I return the following string: "This node contains {0}. There is no next node." .format(self.data)
This is what my __repr__ method looks like right now:
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
This is what the whole linked list class looks like so far:
class MyLinkedList(object):
head = None
size = None
def __init__(self):
self.size = 0
def get_head(self):
return self.head
def get_size(self):
return self.size
def is_empty(self):
if self.size == 0:
return True
else:
return False
def __repr__(self):
result = "["
curr = self.head
while curr != None:
if curr.next == None:
result += curr.data
break
result += curr.data
result += ", "
curr = curr.next
result += "]"
return result
def add_to_head(self, data):
new_node = MyLinkedList.Node(data)
if self.size == 0:
self.head = new_node
else:
new_node.next = self.head
self.head = new_node
self.size += 1
def delete_from_head(self):
if (self.size == 0):
self.head = None
else:
new_head = self.head.next
self.head = new_head
self.size =- 1
class Node():
next = None
def __init__(self, data):
self.data = data
def get_data(self):
return self.data
def get_next(self):
return self.next
def __repr__(self):
if MyLinkedList.get_size() == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)
But now when I try to print the string representation of any node, it gives me the following error: TypeError: get_size() missing 1 required positional argument: 'self'
Is there any way to fix this issue? All I'm trying to do is to call my outer class's get_size() method in my inner node class, and check if that value is 1. Is there any other way to make it so my node's __repr__ method returns the string I want it to return when there's only one node in the list?
Also, if you spot other improvements you could make to my code, I would be glad to hear them.
You can only invoke get_size() on an instance of your MyLinkedList class. A node shouldn't know anything about the linked list class anyway. Just take advantage of the node's next pointer instead:
def __repr__(self):
suffix = "There is not next node" if self.next is None else "The next node is {}".format(self.next.data)
return "This node contains {}. {}.".format(self.data, suffix)
I believe you need to add a self argument into the get_size() method. Such as:
def __repr__(self):
if MyLinkedList.get_size(self) == 1:
return "This node contains {0}. There is no next node." . format(self.data)
return "This node contains {0}. The next node is {1}." .format(self.data, self.next.data)

How to determine if a Binary Tree node is a Left or Right child?

I have a simple Tree data structure, however, I would like to implement two methods named isLeftChild and isRightChild.
The problem is I am having a very hard time understanding trees. The concept and general process has not fully been grasped.
Here is my simple tree so far:
class Node(object):
''' A tree node. '''
def __init__(self, data):
self.left = None
self.right = None
self.data = data
def isLeftChild(self):
''' Returns True if the node is a left child, else False. '''
pass
def isRightChild(self):
''' Returns True if the node is a right child, else False. '''
pass
def insert(self, data):
''' Populate the tree. '''
if self.data:
if data < self.data:
if self.left is None:
self.left = Node(data)
else:
self.left.insert(data)
elif data > self.data:
if self.right is None:
self.right = Node(data)
else:
self.right.insert(data)
else:
self.data = data
def printTree(self):
''' Display the tree. '''
if self.left:
self.left.printTree()
print self.data
if self.right:
self.right.printTree()
def main():
root = Node(8)
root.insert(2)
root.printTree()
main()
How can I have a node determine if it is a left child or a right child (without reference to its data)?
I am not sure what I need to add to my tree in order to determine this.
Use a parent attribute and test if the memory reference if the parent's right or left is the same as the child's reference in memory. You're going to need a parent attribute to traverse the tree anyway.
return self is self.parent.left # in the isLeftChild

Trying to implement simple BFS in python...Not able to print the value if search is successful

Below code is a simple implementation of BFS in Python. I able to print the values level by level from a tree. However when I want to search a element and print it . I am not able to do it. Whts is the error?
def search_bfs(self,root,key):
q=QueueClass()
q.enqueue(root)
while q.size() > 0:
curr_node = q.dequeue()
#print curr_node
#print key
if curr_node == key:
print curr_node
break
if curr_node.left is not None:
q.enqueue(curr_node.left)
if curr_node.right is not None:
q.enqueue(curr_node.right)
from QueueClass import QueueClass
class Node:
def __init__(self,data):
self.data=data
self.left=None
self.right=None
def __str__(self):
return str(self.data)
class searchtree:
def __init__(self):
self.root = None
def create(self,val):
if self.root == None:
self.root=Node(val)
else:
current=self.root
while 1:
if val < current.data:
if current.left:
current=current.left
else:
current.left=Node(val)
break
if val > current.data:
if current.right:
current=current.right
else:
current.right=Node(val)
break
else:
break
tree=searchtree()
lst=[3,1,2,6,4,5,8,12]
for i in lst:
tree.create(i)
tree.search_bfs(tree.root, 3)
You really make it hard to reproduce your problem! So here's what I did:
class QueueClass(object):
def __init__(self):
self.l = []
def size(self): return len(self.l)
def enqueue(self, it): self.l.append(it)
def dequeue(self): return self.l.pop()
class Node:
def __init__(self, name, left=None, right=None):
self.name = name
self.left = left
self.right = right
def __str__(self):
return '{}:{}/{}'.format(self.name, self.left, self.right)
root = Node('root')
adding all the code you omitted (more than you supplied!-).
And now, adding your code exactly as reported, the call:
search_bfs(None, root, root)
emits
root:None/None
exactly as desired and contrary to your report.
It follows that your bug is in some of code you didn't show us, not in the coded you did show.
You either have a buggy queue-class, or are building a different tree than you thought, or searching for a node that is not actually in the tree.
Hard to debug code you're now showing, you know.
Added: so now I've integrated the extra code per your edit and at the end I have:
st = searchtree()
st.create('imtheroot')
st.search_bfs(st.root, st.root)
and of course it prints imtheroot as expected.
Is your bug perhaps STILL hiding in parts you're not yet showing, e.g instead of looking for a node you may be looking for something else?
E.g, if the final call was erroneously st.search_bfs(st.root, 'imtheroot') then obviously the search would fail -- you're checking equality of the key parameter with a node, so key clearly must be a node. not a string or other things (unless the Node class defines a very, very peculiar __eq__ method, which the one you've shown fortunately doesn't:-).
I think the issue is that when you do if curr_node == key, curr_node is a Node object, which has an integer .data attribute, but key is the integer value.
So I think you just need to use if curr_node.data == key.

Categories