'NoneType' object has no attribute 'height' - python

I am trying to find the height of a BST but it is giving error like 'NoneType' object has no attribute 'height'. I can't figure out the error.
class BST:
def __init__(self,val):
self.left = None
self.right = None
self.root = val
def insert(self,data):
if self.root == None:
self.root = BST(data)
elif data > self.root:
if self.right == None:
self.right = BST(data)
else:
self.right.insert(data)
elif data < self.root:
if self.left == None:
self.left = BST(data)
else:
self.left.insert(data)
def inorder(self):
if self.left != None:
self.left.inorder()
print(self.root)
if self.right != None:
self.right.inorder()
def height(self):
if self.root == None:
return 0
else:
return 1 + max(self.left.height(), self.right.height())
t = BST(4)
t.insert(1)
t.insert(7)
t.insert(3)
t.insert(6)
t.insert(2)
t.insert(5)
t.inorder()
print(t.height())

You need to change your init method to be this:
def __init__(self,val):
self.left = None
self.right = None
self.root = val
self.rheight = 0
self.lheight = 0
And your height method to be this:
def height(self):
if self.root == None:
return 0
else:
if hasattr(self.left, 'height'):
self.lheight = self.left.height()
if hasattr(self.right, 'height'):
self.rheight = self.right.height()
return 1 + max(self.lheight, self.rheight)
The reason this needs to change is, you are calling height all the way down your tree, thus getting all the way to None on the right and left sides, all the way at the bottom of the tree. So what this does is check if self.right and self.left have the attribute of height. They won’t if the type is None, so when both are None, we return all the way back out.

When you get to this line
return 1 + max(self.left.height(), self.right.height())
Then at some point, self.left becomes not defined (though not at the very start). You can check this by adding print(self.left) just before that statement, and you will see None outputted just before the error message.
This means that while self.root is defined, your base case needs to includes self.left (and possibly self.right), so that at no point are any of those None.

Replace this line:
return 1 + max(self.left.height(), self.right.height())
with
if hasattr(self.left, 'height'):
left_height = self.left.height()
if hasattr(self.right, 'height'):
right_height = self.right.height()
return 1 + max(left_height, right_height)

Related

Trouble calling height() on a binary tree

I'm writing a function to check if a binary tree satisfies the Height-Balance Property. This is my code but I'm having trouble calling the height function for left and right from my given LinkedBinaryTree class. The main thing that's confusing me is that the nested function takes the root a parameter but height() doesn't. For reference, bin_tree is a LinkedBinaryTree() not a node. Thank you in advance for any help!
My Code
from LinkedBinaryTree import LinkedBinaryTree
def is_height_balanced(bin_tree):
if bin_tree.root is None:
return True
left = bin_tree.height()
right = bin_tree.height()
if abs(left - right) <= 1:
if is_height_balanced(bin_tree.root.left) is True and is_height_balanced(bin_tree.root.right) is True:
return True
return False
Portion of LinkedBinaryTree class
class LinkedBinaryTree:
class Node:
def __init__(self, data, left=None, right=None):
self.data = data
self.parent = None
self.left = left
if (self.left is not None):
self.left.parent = self
self.right = right
if (self.right is not None):
self.right.parent = self
def __init__(self, root=None):
self.root = root
self.size = self.count_nodes()
# assuming count_nodes() and is_empty() works as expected
def height(self):
def subtree_height(root):
if (root.left is None and root.right is None):
return 0
elif (root.left is None):
return 1 + subtree_height(root.right)
elif (root.right is None):
return 1 + subtree_height(root.left)
else:
left_height = subtree_height(root.left)
right_height = subtree_height(root.right)
return 1 + max(left_height, right_height)
if(self.is_empty()):
raise Exception("Tree is empty")
return subtree_height(self.root)

TypeError: __init__() missing 1 required positional argument: 'value'

I know that I need an instance of TernaryTree() to be initially be empty until it is by the generate_tree() method using values in list L. Tried a variety of methods but not sure where to go from here. What do I need to add to get value to pass through?
class TernaryTree(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.mid = None
def insert_node(self, new_value):
if new_value <= self.value: #check if value is lower
if self.left == None: #if value is none
self.left = TernaryTree(new_value) #add in node value
else:
self.left.insert_node(new_value) #go left place node value
elif new_value == self.value: #check if value is the same
if self.mid == None:
self.mid = TernaryTree(new_value)
else:
self.mid.insert_node(new_value)
else: #case when new_value > self.value:
if self.right == None:
self.right = TernaryTree(new_value)
else:
self.right.insert_node(new_value)
def traverse_LMRW(self):
if self.left != None: #alternatively, if self.left:
self.left.traverse_LMRW() #go a level deeper
if self.mid != None:
self.mid.traverse_LMRW()
if self.right != None: #if node on right is not None!
self.right.traverse_LMRW() #go a level deeper
print(self.value)
def leaf_count(self):
if self is None:
return 0
if(self.left is None and self.right is None):
return 1
else:
return leaf_count(self.left) + leaf_count(self.right)
def generate_tree(L):
T = TernaryTree(L[0]) #first element in our list is the root by default
for value in L[1:]:
T.insert_node(value)
return T
def main():
L = [4,1,2,2,3,1,0,4,6,5,6,4]
T = TernaryTree()
T.generate_tree(L)
T.traverse_LMRW()
T.leaf_count()
main()
As per your code generate_tree is not a instance method. So indent out it from class indentation. And call it as python function from there it will create instance of TernaryTree.
In main you were creating instance of TernaryTree without any initial value which is required in __init__ constructor.
Update code will be:
class TernaryTree(object):
def __init__(self, value):
self.value = value
self.left = None
self.right = None
self.mid = None
def insert_node(self, new_value):
if new_value <= self.value: #check if value is lower
if self.left == None: #if value is none
self.left = TernaryTree(new_value) #add in node value
else:
self.left.insert_node(new_value) #go left place node value
elif new_value == self.value: #check if value is the same
if self.mid == None:
self.mid = TernaryTree(new_value)
else:
self.mid.insert_node(new_value)
else: #case when new_value > self.value:
if self.right == None:
self.right = TernaryTree(new_value)
else:
self.right.insert_node(new_value)
def traverse_LMRW(self):
if self.left != None: #alternatively, if self.left:
self.left.traverse_LMRW() #go a level deeper
if self.mid != None:
self.mid.traverse_LMRW()
if self.right != None: #if node on right is not None!
self.right.traverse_LMRW() #go a level deeper
print(self.value)
def leaf_count(self):
if self is None:
return 0
if(self.left is None and self.right is None):
return 1
else:
return leaf_count(self.left) + leaf_count(self.right)
def generate_tree(L):
T = TernaryTree(L[0]) #first element in our list is the root by default
for value in L[1:]:
T.insert_node(value)
return T
def main():
L = [4,1,2,2,3,1,0,4,6,5,6,4]
T = generate_tree(L)
T.traverse_LMRW()
T.leaf_count()
main()
You're receiving this error because you did not pass a value to your TernaryTree constructor. I guess here generate_tree as a classmethod is what you really need.

Cofusing about lookup node with binary tree

I build a binary tree with python code, now I could print it in order with testTree.printInorder(testTree.root). I have tried to lookup some node ,and the function findNode doesn't work anymore . print testTree.findNode(testTree.root,20) whatever I put in just return None.
class TreeNode:
def __init__(self, value):
self.left = None;
self.right = None;
self.data = value;
class Tree:
def __init__(self):
self.root = None
def addNode(self,node,value):
if node == None:
self.root = TreeNode(value)
else:
if value < node.data:
if node.left == None:
node.left = TreeNode(value)
else:
self.addNode(node.left,value)
else:
if node.right == None:
node.right = TreeNode(value)
else:
self.addNode(node.right,value)
def printInorder(self,node):
if node != None:
self.printInorder(node.left)
print node.data
self.printInorder(node.right)
def findNode(self,node,value):
if self.root != None:
if value == node.data:
return node.data
elif value < node.data and node.left != None:
self.findNode(node.left,value)
elif value > node.data and node.right != None:
self.findNode(node.right,value)
else:
return None
testTree = Tree()
testTree.addNode(testTree.root, 200)
testTree.addNode(testTree.root, 300)
testTree.addNode(testTree.root, 100)
testTree.addNode(testTree.root, 30)
testTree.addNode(testTree.root, 20)
#testTree.printInorder(testTree.root)
print testTree.findNode(testTree.root,20)
Any function without an explicit return will return None.
You have not returned the recursive calls within findNode. So, here.
if value == node.data:
return node.data
elif value < node.data and node.left != None:
return self.findNode(node.left,value)
elif value > node.data and node.right != None:
return self.findNode(node.right,value)
Now, I can't help but thinking this is a bit noisy. You'll always start adding from the root, yes?
testTree.addNode(testTree.root, 200)
You could rather do this
testTree.addNode(200)
And to do that, you basically implement your methods on the TreeNode class instead. So, for the addNode.
You could also "return up" from the recursion, rather than "pass down" the nodes as parameters.
class TreeNode:
def __init__(self, value):
self.left = None
self.right = None
self.data = value
def addNode(self,value):
if self.data == None: # Ideally, should never end-up here
self.data = value
else:
if value < self.data:
if self.left == None:
self.left = TreeNode(value)
else:
self.left = self.left.addNode(value)
else:
if self.right == None:
self.right = TreeNode(value)
else:
self.right = self.right.addNode(value)
return self # Return back up the recursion
Then, in the Tree class, just delegate the addNode responsibility to the root
class Tree:
def __init__(self):
self.root = None
def addNode(self,value):
if self.root == None:
self.root = TreeNode(value)
else:
self.root = self.root.addNode(value)
When you recurse to children in findNode you need to return the result, otherwise the function will implicitly return None:
def findNode(self,node,value):
if self.root != None:
if value == node.data:
return node.data
elif value < node.data and node.left != None:
return self.findNode(node.left,value) # Added return
elif value > node.data and node.right != None:
return self.findNode(node.right,value) # Added return
else:
return None

Returning value of None object

I have got adding method in my AVL tree implementation.
class Node:
def __init__(self, data, left = None, right = None, height = -1):
self.data = data
self.left = left
self.right = right
self.height = height
def addNode(self, data):
if self.data > data:
'''Adding to left subtree'''
if self.left is None:
self.left = Node(data)
else:
self.left.addNode(data)
if ( abs(self.right.height - self.left.height) == 2):
''' Then we need to balance a subtree'''
print("Rebalancing after inserting", data)
if (data < self.left.data):
self.rotateLeft()
else:
self.doubleRotateLeft()
elif self.data < data:
'''Adding to right subtree'''
if self.right is None:
self.right = Node(data)
else:
self.right.addNode(data)
if ( abs(self.right.height - self.left.height) == 2):
''' Then we need to balance a subtree'''
print("Rebalancing after inserting", data)
if (data < self.right.data):
self.rotateRight()
else:
self.doubleRotateRight()
But when I try to do
self.right.height
and self hasn't got right object then it will not return height, even default value is -1. How can I fix it? I am trying to make as less duplicated code in this method as possible
If self.right is set to None you can't use self.right.height, no. Don't use None if that expression must work. Use a sentinel that defines that attribute, instead.
A sentinel can be just a custom class that has no value and no child nodes. You can make that a singleton just like None:
class Sentinel(object):
value = left = right = None
height = -1
sentinel = Sentinel()
class Node:
def __init__(self, data, left=sentinel, right=sentinel, height=-1):
# ...
then test for is sentinel instead of is None in your code. By using sentinel as the default value for the left and right keyword arguments, self.left.height and self.right.height will always work (provided self is an instance of Node).
Don't forget to increment height each time you create a new node though.
You can simplify your addNone() method a little by using local references:
def addNode(self, data):
if self.data == data: return
left = self.data > data
testnode = self.left if left else self.right
if testnode is sentinel:
node = Node(data)
setattr(self, 'left' if left else 'right', node)
else:
testnode.addNode(data)
if abs(self.right.height - self.left.height) == 2:
if data < testnode.data:
rotation = self.rotateLeft if left else self.rotateRight
else:
rotation = self.doubleRotateLeft if left else self.doubleRotateRight
rotation()

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