Binary Tree Size function in Python - 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

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.

Error on checking if a tree is a Binary Search Tree

I am currently trying to check if a tree is a BST, while keeping notice of the fact that the values must not be equal to any other one in the tree. I tried keeping count of the interval on which each value should be ( considering a min and a max as arg[0] and arg[1]).
If we are for example going all the way down on the left subtree, there will be no min, only a max. However, when we switch to the right, we will also have a minimum ( the value of the root node we just switched right from).
However, my code is not showing the right answer and i have no idea why. Could you please help me?
These are my functions: ( i am resolving this on hackerrank therefore that's why i have two functions instead of one)
""" Node is defined as
class node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
"""
def check_binary_search_tree_(root):
check_bst(root,None,None)
def check_bst(root,*arg):
res, res2 = True, True
if arg[0] is None and arg[1] is not None:
if root.data >=arg[1]:
return False
elif arg[1] is None and arg[0] is not None:
if root.data <= arg[0]:
return False
elif arg[1] is not None and arg[0] is not None and (root.data<=arg[0] or root.data >= arg[1]):
return False
if root.left:
res = check_bst(root.left, arg[0], root.data)
if root.right:
res2= check_bst(root.right, root.data, arg[1])
if not res or not res2:
return False
return True
Your problem here is that you don't have the check_binary_search_tree_ function that HackerRank calls returning anything. Instead of this
def check_binary_search_tree_(root):
check_bst(root,None,None)
you should be doing this
def check_binary_search_tree_(root):
return check_bst(root,None,None)

Function to determine whether tree is a valid BST?

I have to determine whether given a list representing a tree, whether the tree is a valid BST (this question is taken from leetcode). I have seen other posts on this but I was wondering if someone could help me with my approach, since it is clearly not right. For example, for the tree [1,2,3] where 1 is the root, 2 is the left child, and 3 is the right child, my code returns true. Hopefully it only requires small changes, but it might be that the entire function's approach is incorrect.
Here is my code:
def isValidBST(self, root):
if (root == None):
return True
if (root.left == None or root.left.val < root.val):
return self.isValidBST(root.left)
if (root.right == None or root.right.val > root.val):
return self.isValidBST(root.right)
return False
Secondly, I have seen approaches with a helper function that takes in a min/max value, but that confuses me. If anyone would also like to explain why that approach is a good/better one, that would be greatly appreciated!
I'd make a min_max method for Nodes that finds the min and max values of the tree rooted at that Node. Do sanity checking while finding those, and then isValidBST can just catch the exception
def max_min(self):
'''
Returns maximum and minimum values of the keys of the tree rooted at self.
Throws an exception if the results are not correct for a BST
'''
l_max, l_min = self.left.max_min() if self.left else (self.val, self.val)
if l_max > self.val:
raise ValueError('Not a BST')
r_max, r_min = self.right.max_min() if self.right else (self.val, self.val)
if r_min < self.val:
raise ValueError('Not a BST')
return l_min, r_max
def isValidBST(self):
try:
if self.max_min():
return True
except ValueError:
return False
Here is one way to implement the validity check:
class BST:
def __init__(self, value, left=None, right=None):
self.value = value
self.left = left
self.right = right
def isValidBST(self):
'''
Simultaneously check for validity and set our own min and max values.
'''
self.min = self.max = self.value
if self.left:
if not self.left.isValidBST():
return False
if self.left.max >= self.value:
return False
self.min = self.left.min
if self.right:
if not self.right.isValidBST():
return False
if self.right.min < self.value:
return False
self.max = self.right.max
return True
assert BST(2, BST(1), BST(3)).isValidBST()
case = BST(2, BST(1, None, BST(3)))
assert case.left.isValidBST()
assert not case.isValidBST()

Height of binary tree

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

Print a binary tree, python, in inorder

Me and my friend are doing some school work with programming in Python 3.1 and are VERY stuck. We're programming a binary tree and it's working fine except when we want to print all the nodes in inorder in a way that would create a sentence (all the words in inorder just after one another in a row). We have been looking all over the internet for clues as to how to procede and we've been working with this little thing for like two hours. Any advice/help would be awesome.
Our program/Binary tree:
class Treenode:
def __init__(self, it = None, le = None, ri = None):
self.item = it
self.left = le
self.right = ri
class Bintree:
def __init__(self):
self.item = None
self.left = None
self.right = None
def put(self, it = None):
key = Treenode(it)
if self.item == None:
self.item = key
return
p = self.item
while True:
if key.item < p.item:
if p.left == None:
p.left = key
return
else:
p = p.left
elif key.item > p.item:
if p.right == None:
p.right = key
return
else:
p = p.right
else:
return
def exists(self, it):
key = it
p = self.item
if p == key:
return True
while True:
if key < p.item:
if p.left == None:
return False
else:
p = p.left
elif key > p.item:
if p.right == None:
return False
else:
p = p.right
else:
return
def isEmpty(self):
if self.item == None:
return True
else:
return False
def printtree (Treenode):
if Treenode.left != None:
printtree (Treenode.left)
print (Treenode.item)
if Treenode.right != None:
printtree (Treenode.right)
We get a sort of print when we run the program which looks like this: "bintree.Treenode object at 0x02774CB0", which is not what we want.
We use the tree by running this:
import bintree
tree = bintree.Bintree()
print(tree.isEmpty()) # should give True
tree.put("solen")
print(tree.isEmpty()) # should give False
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
tree.put("upp")
tree.put("himlarunden")
tree.put("manen")
tree.put("seglar")
tree.put("som")
tree.put("en")
tree.put("svan")
tree.put("uti")
tree.put("midnattsstuden")
print(tree.exists("visa")) # should give False
print(tree.exists("ban")) # should give True
tree.printtree() # print sorted
Also, the second last row gives us "None" instead of "True", which is wierd.
To print a binary tree, if you are printing a leaf you just print the value; otherwise, you print the left child then the right child.
def print_tree(tree):
if tree:
print tree.value
print_tree(tree.left)
print_tree(tree.right)
print(tree.exists("visa")) returns None, because in the last line of exists() there's return statement without any value (which defaults to None).
Also you shouldn't name a printtree argument Treenode since it's a name of an existing class and that might lead to confusion. It should look more like:
def printtree(tree_node):
if tree_node.left is not None:
printtree(tree_node.left)
print(tree_node.item)
if tree_node.right is not None:
printtree(tree_node.right)
Another thing is calling printtree - it's a function, not Bintree method, so I suppose you should call it printtree(tree).
One way to make testing easier is to use -assert()- instead of printing things and then referring back to your code.
tree = Bintree()
assert(tree.isEmpty())
tree.put("solen")
assert(not tree.isEmpty())
tree.put("gott")
tree.put("sin")
tree.put("hela")
tree.put("ban")
http://docs.python.org/reference/simple_stmts.html#the-assert-statement
It raises an error if its condition is not true. I know that doesn't fix your bug but making things less ambiguous always helps debugging.
You are not specifying a starting case for printtree(). You're defining how to recurse through your tree correctly, but your call to printtree() has no node to start at. Try setting a default check to see if a parameter is passed in, and if one isn't start at the head node of the bintree.
The reason your second to last line is printing None is because, in your exists method, you just have a "return", rather than a "return True", for the case of finding a `p.item' that is equal to key.

Categories