Height of binary tree - python

I'm trying to implement a recursive method to calculate the height of a binary tree. Here is the "height"-code:
def height(self):
if self.root==None:
return 0
return max(height(self.root.left), height(self.root.right))+1
When I try to call the function, I get the following error msg:
NameError: name 'height' is not defined
Does anybody see the problem?

This is a method of your class, hence you must call it from an instance (self) or the class itself. Though it won't work as you think, unless you define it as a staticmethod or change your call, e.g.
def height(self):
return 1 + max(self.left.height() if self.left is not None else 0,
self.right.height() if self.right is not None else 0)
or
#staticmethod
def height(self):
return 1 + max(self.height(self.left) if self.left is not None else 0,
self.height(self.right) if self.right is not None else 0)
Notice, that you shouldn't use == to compare with None (kudos to timgeb). And you must check whether child-nodes exist, too. And your algorithm doesn't work, so I've changed it slightly.
Example:
class Node:
def __init__(self, root=None, left=None, right=None):
self.root = root
self.left = left
self.right = right
def height(self):
return 1 + max(self.left.height() if self.left is not None else 0,
self.right.height() if self.right is not None else 0)
# Create a binary tree of height 4 using the binary-heap property
tree = [Node() for _ in range(10)]
root = tree[0]
for i in range(len(tree)):
l_child_idx, r_child_idx = (i + 1) * 2 - 1, (i + 1) * 2
root_idx = (i + 1) // 2
if root_idx:
tree[i].root = tree[root_idx]
if l_child_idx < len(tree):
tree[i].left = tree[l_child_idx]
if r_child_idx < len(tree):
tree[i].right = tree[r_child_idx]
print(root.height()) # -> 4

I am not sure of how you define your binary tree. But on a tree node you usually have only one root and multiple sons. I have the feeling that this method leads to an infinite loop. self.root.left and self.root.right are exactly my brother and me...
Here you probably have to call the method from the instances self.root.left and self.root.right with no extra argument :
def height(self):
if self.root==None:
return 0
return max(self.root.left.height(), self.root.right.height())+1

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)

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

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

Binary Tree Size function in Python

I have written a couple of functions to calculate size of binary tree. The first one (Function 1) works perfectly fine and is declared outside the class, it is not a member function of the class. However the second one which is the member function of the class is giving me weird results. I am confused! Any help would be appreciated.
Function 1
def size(root):
if root is None:
return 0
else:
return size(root.left)+ 1+ size(root.right)
Function 2
def size(self):
if self.left is None or self.right is None:
return 0
else:
return self.left.size()+1+self.right.size()
if self.left is None or self.right is None:
if one of them is None, return 0
you need to at least get the size of the right + 1 if left is None
I think you need something like :
leftSize = self.left.size() if self.left else 0
rightSize = self.right.size() if self.right else 0
return leftSize + 1 + rightSize
I havent tried it.
The second function is not accurate, for example
x = Tree()
x.left = Tree()
x.right = None
In the above example, x.size() would evaluate to zero, since you are considering either left is None or right is None to return 0 (which is trivially true for the above example). You need to tweak your logic.
def size(self):
total = 1 #any instantiated object has a size of at least 1.
if self.left is not None: #feel free to add additional validity checks, i.e. instanceof(Tree...)
total += self.left.size()
if self.right is not None:
total += self.right.size()
return total

Python Binary Tree print nodes with two exactly two children

How do I make a function that returns the number of nodes in a tree that have two children?
My class code is as follows:
class RefBinaryTree:
def __init__(self, data, left=None, right=None):
self.key = data
self.left = left
self.right = right
def insert_left(self, value):
self.left = RefBinaryTree(value, left=self.left)
def insert_right(self, value):
self.right = RefBinaryTree(value, right=self.right)
def get_left_subtree(self):
return self.left
def get_right_subtree(self):
return self.right
def set_value(self, new_value):
self.key = new_value
def get_value(self):
return self.key
def create_string(self, indent):
string = str(self.key) + '---+'
if self.left:
string += '\n(l)' + indent + self.left.create_string(indent + ' ')
if self.right:
string += '\n(r)' + indent + self.right.create_string(indent + ' ')
return string
def __str__(self):
return self.create_string(' ')
I'm guessing it would be best to use recursion. Any hints or helpful links would be awesome. Thanks.
It's really quite simple to count two-child nodes recursively. If you return a number with each function call (zero as the base case) you can simply add 1 every time you find a two-child node:
def findDoubleNodes(tree):
if tree == None or (tree.left == None and tree.right == None):
# base case
return 0
elif tree.left <> None and tree.right <> None:
# both have children, so add one to our total and go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right) + 1
else:
# only one child, so only go down one level
return findDoubleNodes(tree.left)+findDoubleNodes(tree.right)
Inputting a RefBinaryTree returns the number of nodes with two children. An example:
x = RefBinaryTree(1)
x.insert_left(5)
x.left.insert_left(6)
x.left.insert_right(7)
x.left.right.insert_left(8)
x.left.right.insert_right(9)
x.left.right.right.insert_right(10)
The (lazily) created tree looks like this:
1
/
5
/ \
6 7
/ \
8 9
\
10
And findDoubleNodes(x) returns 2, as only two nodes (5 and 7) have two children.
Additionally, adding a left child to node 9 (x.left.right.right.insert_left(11)) has the expected result, returning 3.
This should do:
def countNodes(tree):
if tree is None:
return 0
left = tree.get_left_subtree()
rght = tree.get_right_subtree()
return (0 if left is None or rght is None else 1) \
+ countNodes(left) + countNodes(rght)

Categories