Tree Inorder Traversal Python - 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!

Related

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.

AttributeError: 'NoneType' object has no attribute height in BST pythons height

This is my code for creating a BST in python everything is working fine but when i accesses height function it gives error like "AttributeError: 'NoneType' object has no attribute height"i am new to creating datastructures in python any help will be appriciated
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 print_tree(self):
if self.left:
self.left.print_tree()
print (self.data)
if self.right:
self.right.print_tree()
def height(self):
if self.data is None:
return 0
else:
return 1 + max(self.left.height(),self.right.height())
root = Node(8)
root.insert(3)
root.insert(10)
root.insert(1)
root.insert(6)
root.insert(4)
root.insert(7)
root.insert(14)
root.insert(13)
root.print_tree()
root.height()
For leaf nodes in your tree, self.data would be set to the value of the leaf node , but self.left and self.right would be None. But even before that, there can be nodes where either left or right child is None and since we try that node and get its height we get the AttributeError on NoneType .
In the code for height -
def height(self):
if self.data is None:
return 0
else:
return 1 + max(self.left.height(),self.right.height())
when the recursion reaches a node, where either left or right node is None, the above code would fail because it would try to access self.left.height() or self.right.height() , and one of them is None.
We can add a simple check to see if either self.left or self.right is None , and based on that get the height from the it.
If you get an error like that it means you try to use a field of a None. Because your tree is finite you have a leaf that doesn't have any data. I see you have a place in the code where self.data field is assigned to a value but self.left and self.right don't. At the same time, you get values of the fields in height method, you check only self.data. That doesn't make sense for me.
Also, I suggest trying pdb or another debugging tool.

Insertion into a Binary Search Tree

This is the code I came up with to insert a new value into a BST:
class BST(object):
def __init__(self, root):
self.root = Node(root)
def insert(self, new_val):
self.__internal_insert(self.root, new_val)
def __internal_insert(self, node, new_val):
if node is None:
node = Node(new_val)
elif new_val < node.value:
self.__internal_insert(node.left, new_val)
else:
self.__internal_insert(node.right, new_val)
# Set up the tree
tree = BST(4)
# Insert elements
tree.insert(2)
tree.insert(1)
tree.insert(3)
tree.insert(5)
however, while debugging I noticed that the self.root is never updated, eg.: as soon as the __internal_insert() method finishes and a new insert() is performed, its sibling nodes left and right are back to None instead to the previous value that was set.
Hope you help me spot where the bug is. I picked up Python recently, my apologizes if this is a trivial question.
You defined node = Node(new_val), but then where do you ever attach that node to it's parent?
Basically, you recurse, but never captured the results you're building
Try returning the node you created
def __internal_insert(self, node, new_val):
if node is None:
node = Node(new_val)
elif new_val < node.value:
node.left = self.__internal_insert(node.left, new_val)
else:
node.right = self.__internal_insert(node.right, new_val)
return node
I see two issues with the code.
First, when you reassign node, it is not assigning the value to the BST object. To do that, you need to reference it directly: self.left = Node(new_val).
Second, you're not checking the equals condition, so your code isn't going to work like a traditional BST. You'll end up with a lot of extraneous nodes.

Issue with the delete method of a Binary Search Tree in 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)

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

Categories