Returning value of None object - python

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()

Related

How can I convert an array of integers into a BST? - in particular how can I initialise the BST, then add nodes to this root?

I am given an array of integers and I would like to convert into a BST;
class BST:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def insert(self, value):
if value<self.value:
if not self.left:
self.left = BST(value)
else:
self.left.insert(value)
else:
if not self.right:
self.right = BST(value)
else:
self.right.insert(value)
return self
array = [3,10,5,2,7,6,11]
def insertArrayEntryIntoBst(array):
currentNode = BST()
for i in range(len(array)):
currentNode.insert(array[i])
Challenges that I have:
How do I initialise the BST? - in the insert() function do I need to start with a line that reads if not currentNode: BST(array[0])?
After initialising, is my code correct for insertArrayEntryIntoBst()? The idea is simply to loop through the input array and let the insert() function do its magic.
Do I need a value argument in this case? - since the integer value in the array will represent both the node and its value? (which will always be the same thing)
You may construct first node outside loop with the first item of the array.
If you need to access the node. So it can be returned as well.
class BST:
def __init__(self,value):
self.right = None
self.left = None
self.value = value
def insert(self, value):
if value<self.value:
if not self.left:
self.left = BST(value)
else:
self.left.insert(value)
else:
if not self.right:
self.right = BST(value)
else:
self.right.insert(value)
return self
def insertArrayEntryIntoBst(array):
currentNode = BST(array[0])
for i in range(1,len(array)):
currentNode.insert(array[i])
return(currentNode)
array = [3,10,5,2,7,6,11]
myNode=insertArrayEntryIntoBst(array)
print(myNode.value);
print(myNode.left.value);
print(myNode.right.value);

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.

'NoneType' object has no attribute 'height'

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)

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.

Categories