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)
Related
I am trying to understand the recursive call within the binary function is_bst. The goal of the function is to check if a tree is a binary search tree or not. Note, This is just an excerpt from the complete function. I am trying to understand what is happening on this block of code is_bst_l, min_l, max_l = is_bst(node.left) in the is_bst function. The is_bst function return (True, None, None), I am trying to figure out how the function came up with the return value by going through the function recursively. The is_bst function takes as input the binary tree node from the parse_tuple function. As an example, when I try to unpack this line of code is_bst_l, min_l, max_l = node.left outside the is_bst function I get TypeError: cannot unpack non-iterable TreeNode object, but within the function, I don't get an error. My Question is
what is happening recursively within the is_bst function.
How can I don't get the unpack TypeError within the is_bst function.
`
#Tree Class
class TreeNode:
def __init__(self, key):
self.key = key
self.left = None
self.right = None
#function converts a turble to a binary tree
def parse_tuple(data):
# print(data)
if isinstance(data, tuple) and len(data) == 3:
node = TreeNode(data[1])
node.left = parse_tuple(data[0])
node.right = parse_tuple(data[2])
elif data is None:
node = None
else:
node = TreeNode(data)
return node
Function Checks if a tree is a Binary search tree or not.
def remove_none(nums):
return [x for x in nums if x is not None]
def is_bst(node):
if node is None:
return True, None, None
is_bst_l, min_l, max_l = is_bst(node.left)
is_bst_r, min_r, max_r = is_bst(node.right)
is_bst_node = (is_bst_l and is_bst_r and
(max_l is None or node.key > max_l) and
(min_r is None or node.key < min_r))
# print('Minkey left: ', min_l, 'Nodekey :', node.key, 'Maxkey left: ',min_r)
# print('Minkey left: ', max_l, 'Nodekey :', node.key, 'Maxkey left: ',max_r)
min_key = min(remove_none([min_l, node.key, min_r]))
max_key = max(remove_none([max_l, node.key, max_r]))
# print(node.key, min_key, max_key, is_bst_node)
return is_bst_node, min_key, max_key
#function call
my_tuple= ((None,3,None),2,(None,5,None))
node = parse_tuple(my_tuple)
The function is returning the maximum and minimum key on the left and right of the current node (in addition to the True/False result of the check). Its recursion assembles these keys / validity states from the left and right nodes creating the need to manage (i.e. exclude) None results from the subnodes returned value.
This is overly complex, and probably not worth your time to analyze and understand.
I think you'll find this one a bit more straightforward:
def is_bst(node,minKey=None,maxKey=None):
if node is None: return True
if minKey is not None and node.key<=minKey: return False
if maxKey is not None and node.key>=maxKey: return False
if not is_bst(node.left,minKey,self.key): return False
if not is_bst(node.right,self.key,maxKey): return False
return True
A tree is a BST if recursively for every node:
Its left tree if it exists is a BST
Its right tree if it exists is a BST
The largest value of the left tree, if it exists, must be smaller than the node's value
The smallest value of the right tree, if it exists, must be larger than the node's value
Hence it makes sense for the return value to the the tuple of three values:
Am I a BST?
What is the smallest node in my subtree
What is the largest node in my subtree.
When is_bst(node.left) or is_bst(node.right) returns "None" for max_l,_min_l or max_r,min_r the following hapens:
condition = (max_l is None) or (node.key > max_l)
condition1 : (max_l is None)
condition2 : (node.key >max_l)
Case1:
is_bst_l, min_l, max_l = is_bst(True,None,None)
condition = condition1 or condition2
condition = True or condition2 will not be processed since condition1 is "False"
condition = True because of condition1
Case2:
is_bst_l, min_l, max_l = is_bst(True,1,1)
condition = condition1 or condition2
condition = False or condition2 will be processed since condition1 is not "True"
condition = True because of condition2
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.
class TreeNode:
def __init__(self, _val):
self._val = _val
self._right = None
self._left = None
def search(self, val):
if self._val == val:
value = self._val
return self
if self._right is not None:
right = self._right._val
return self._right.search(val)
if self._left is not None:
left = self._left._val
return self._left.search(val)
EXAMPLE CALL:
a = TreeNode(3)
b = TreeNode(2)
c = TreeNode(4)
a.search(4)
So this just defines a treenode class, and search is supposed to take a val and simply search for it when called on the ROOT of a binary tree. Upon running a testcase, it sometimes returns the correct node, sometimes it returns false when I know it's in the tree. Anyone know what might be causing this inconsistency?
a.search(3) will work, because a's value is 3. For anything else, it shouldn't because you never allowed the nodes to connect up. For each of these nodes, at least based on the code given, don't have left or right branches. They are all initialized with None and never set to anything else. You should state the root branch's left and right and then it should work.
For instance, your class should have this:
def setLeft(self, node):
self._left = node
def setRight(self, node):
self._right = node
Then use it as such:
a = TreeNode(3)
b = TreeNode(2)
c = TreeNode(4)
a.setLeft(b)
a.setRight(c)
a.search(4)
Of course, you should change it depending on how you wanted to structure the tree.
Problems
If searching self._right returns None (the value isn’t anywhere on the right branch), your search will immediately return, never getting to traverse self._left. In fact, as long as self._right exists (is not None), the search method never traverses self._left.
Also, you need to make sure to link up your nodes before running! For example, a._left = c.
Solution
To fix this, I’d implement the search as follows:
def search(self, val):
if self._val = val:
return self
if self._right is not None:
right = self._right.search(val)
if right is not None:
return right # it’s okay to return now, we found something!
if self._left is not None: # if we’ve made it here, _right.search didn’t return
return self._left.search(val)
return None # it’s good practice to explicitly return
if self._right is not None:
It will stop the search and return the result of search right if with self._right, and no more search for the left side.
Here, I revised code and do test.
import random
class Node():
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def search(self, value):
if self.value == value:
return self
result = None
if self.left:
result = self.left.search(value)
if result:
return result
if self.right:
result = self.right.search(value)
return result
def __repr__(self):
return f'<Node value={self.value}>'
tree = {}
tree[0] = Node(0)
for i in range(1, 11):
while True:
parent = tree[random.choice(list(tree))]
position = random.choice([0, 1])
if (position==0 and parent.left) or (position==1 and parent.right):
continue
node = Node(i)
tree[i] = node
if position:
parent.right = node
else:
parent.left = node
break
root = tree[0]
for i in range(-5, 16):
if i in tree:
print(f'Value {i} found in {root.search(i)}')
else:
print(f'Value {i} not found')
Value -5 not found
Value -4 not found
Value -3 not found
Value -2 not found
Value -1 not found
Value 0 found in <Node value=0>
Value 1 found in <Node value=1>
Value 2 found in <Node value=2>
Value 3 found in <Node value=3>
Value 4 found in <Node value=4>
Value 5 found in <Node value=5>
Value 6 found in <Node value=6>
Value 7 found in <Node value=7>
Value 8 found in <Node value=8>
Value 9 found in <Node value=9>
Value 10 found in <Node value=10>
Value 11 not found
Value 12 not found
Value 13 not found
Value 14 not found
Value 15 not found
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()
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.