I am trying to understand below recursion function which says whether a particular node exists in a binary tree. I did my homework and got most of the recursion part but the last return statement (return root.value == value or inleft or inright) bothers me.
can someone please help me in understanding this method?
def existsInTree(self, root, value):
if root is None:
return False
else:
inleft = self.existsInTree(root.left, value)
inright = self.existsInTree(root.right, value)
print(inleft,inright)
return root.value == value or inleft or inright
example binary tree:
10
/ \
11 9
we will first compare data of root with data of node to be searched. If the match is found, set the flag to true. Else, search the node in left subtree and then in the right subtree.
There's another way of looking at that return statement, you can split the return statement at the or keyword
def ifRootExists(self,root, value):
if (root == None):
return False
if (root.value == value):
return True
""" then recur on left sutree """
res1 = ifrootExists(root.left, value)
# root found, no need to look further
if res1:
return True
""" root is not found in left,
so recur on right subtree """
res2 = ifrootExists(root.right, value)
return res2
We can get the result from the above function whether some node exists.
The algorithm is as follows.
root is None or Not. If None, get back the position which the parent function called with the value of "False".
Otherwise, the function continuously search based on the current node.
inleft is a value of function "existsInTree" in which the current node's left child is the root node.
inright is a value of function "existsInTree" in which the current node's right child is the root node.
Let's assume that we want to search value as called V.
Which V exists in the tree means the current value is V or in the left tree, or in the right tree.
To summarize, inleft and inright means whether V includes or not in the subtree.
Related
I have a homework problem and I would really be grateful if someone can give me some help with my learning.
This is the question:
#One concept you will encounter in future CS class is the
#idea of a binary tree.
#
#A binary tree is made of nodes. Each node has three
#attributes: its own value, a left branch, and a right branch.
#The left branch will be lower than the node's own value, and
#the right branch will be higher than the node's own value.
#
#Some nodes will not have any branches; these are called leaf
#nodes. They only have their own value. Some nodes may have
#only one branch as well.
#
#Every binary tree has a single root node at the top of the
#tree. Most algorithms that operate on the tree will start at
#this root node.
#
#For example, let us imagine a binary tree with seven nodes.
#The top node's value is 10. The top node has two child nodes:
#the left node's value is 5, lower than 10. The right node's
#value is 15, higher than 10. Then, the left node has its own
#left and right nodes, with values 3 and 7: the lower and higher
#than 5 respectively, but both lower than 10 because they come
#from the original node's left (lower) branch. The right node's
#left and right branches have values 12 and 18, again lower
#and higher than 15 but both higher than 10.
#
#Below is the code for a single node. Right function called
#binary_tree_search. binary_tree_search should take two
#parameters: a single node, and a search value. It should return
#True if the search value is found anywhere in the tree with
#the node at the top, and False if the search value is not found.
#
#To do this, you'll want to write a function that goes down the
#tree similar to a binary search. If the search value is lower than
#the current node's value, it should continue searching to the
#left. If the search value is higher than the current node's value,
#it should continue searching to the right. If the search value is
#equal to the current node's value, it should return True. If the
#current node has no children (both left and right are None), it
#should return False as it has reached the bottom of the tree.
#
#You may assume that no two nodes will have the same value, and that
#every node will have either two children or none. You should not
#assume that the tree will have 7 nodes; it may have 3, 7, 15, 31,
#or more.
#
#HINT: Try breaking this into cases. What do you do if the node
#has the right value? What if the node is none? What if the node's
#value is higher than the search term? What if it's lower?
#
#HINT 2: To get around not knowing how big the tree will be,
#think about a process you can repeat over and over until either
#you find the search term or reach a leaf node. To repeat that
#process, you'd apply the same reasoning each time, just changing
#what node you're looking at.
class Node:
def __init__(self, value, left = None, right = None):
self.value = value
self.left = left
self.right = right
#Write your binary_tree_search function here!
And here is my code:
def binary_tree_search(node, node_value):
#print(node.value)
if node is None:
return False
elif node.value is node_value:
return True
elif node_value < node.value:
#print("This is the left node: ", node.left.value)
binary_tree_search(node.left, node_value)
elif node_value > node.value:
#print("This is the right node: ", node.right.value)
binary_tree_search(node.right, node_value)
else:
return False
These are the test cases:
#Below are some lines of code that will test your function.
#You can change the value of the variable(s) to test your
#function with different inputs.
#
#If your function works correctly, this will originally
#print: True, True, True, True, False, False, False
#(each on a separate line)
root_node = Node(10)
root_node.left = Node(5)
root_node.right = Node(15)
root_node.left.left = Node(3)
root_node.left.right = Node(7)
root_node.right.left = Node(12)
root_node.right.right = Node(18)
print(binary_tree_search(root_node, 18))
print(binary_tree_search(root_node, 7))
print(binary_tree_search(root_node, 15))
print(binary_tree_search(root_node, 10))
print(binary_tree_search(root_node, 1))
print(binary_tree_search(root_node, 11))
print(binary_tree_search(root_node, 21))
Here are the expected results
True
True
True
True
False
False
False
Here are my actual results:
None
None
None
True
None
None
None
you successfully implement recursion in two of the if cases, however you forget to return the result of this recursion:
add a "return " before each of the cases and everything should work:
# [...]
elif node_value < node.value:
#print("This is the left node: ", node.left.value)
return binary_tree_search(node.left, node_value)
elif node_value > node.value:
#print("This is the right node: ", node.right.value)
return binary_tree_search(node.right, node_value)
Elsewise, python sees no return statement for these cases and defaults to a return value of none (see e.g. Why does my recursive function return None?)
I am looking into the LeetCode problem Search in a Binary Search Tree:
You are given the root of a binary search tree (BST) and an integer val.
Find the node in the BST that the node's value equals val and return the subtree rooted with that node. If such a node does not exist, return null.
[...]
Example 2:
Input: root = [4,2,7,1,3], val = 5
Output: []
I am not sure why this code below will work in Python:
def searchBST(self, root: Optional[TreeNode], val: int) -> Optional[TreeNode]:
#base case
if not root: return None
if root.val == val: return root
#recursion relation
if val > root.val:
return self.searchBST(root.right, val)
else:
return self.searchBST(root.left, val)
Specifically, in the question it says (1) if Tree is null, then we need to return [], (2) If value is not in the tree, we need to return null
Then for the line of code if not root: return None, does it cater to (1)? Aren’t we required to return []?
in the question it says (1) if Tree is null, then we need to return [], (2) If value is not in the tree, we need to return null
That is not entirely true. It says that we need to return null, but in the example it represents the output as [].
It is understandable that this leads to confusion, but it should be noted that LeetCode formats (serializes) the input and the output as lists (JSON notation), even though the actual input and output that your function deals with are not lists. The function gets a node instance as argument (or None), and the function is to return such a reference (or None).
The LeetCode framework takes care of conversion of the text-based input into a node reference before calling your function, and does the reverse when dealing with the value that your function returns. In particular, when your function returns None, that will serialise to [] -- which represents an empty tree.
I am doing Leet Code question 572. Subtree of Another Tree:
Given the roots of two binary trees root and subRoot, return true if there is a subtree of root with the same structure and node values of subRoot and false otherwise.
A subtree of a binary tree tree is a tree that consists of a node in tree and all of this node's descendants. The tree tree could also be considered as a subtree of itself.
I had a working solution. However, when trying a slightly different approach, I ran into a problem with my recursive dfs function. The structure of my code is this:
def isSubTree(self, root, subRoot):
def dfs(root, subRoot):
# some function that returns True when certain conditions are met
if not root: return
self.isSubtree(root.left, subRoot)
self.isSubtree(root.right, subRoot)
if root.val == subRoot.val:
if dfs(root, subRoot):
return True
return False
Basically, isSubTree explores all the nodes in the tree. Once it finds a node with the same value as subRoot, it compares the sub tree rooted at that node with the sub tree rooted at subRoot with dfs().
I intended that when the dfs() function returns true, isSubTree() will also return true. If I've explored all the nodes in the tree (isSubTree() reaches the end) and dfs() hasn't returned true
at all, isSubTree() will return False.
However,my code always returns false seemingly because of the last line where it returns False (I've tested it and can verify that the return True part in if dfs() was reached, also I'm pretty sure my dfs() function is correct).
My question is, is there an elegant way to have my code do what I want it to do?
It is hard to see which code you are using, since obviously there is a syntax problem just below def dfs. Either the indentation is off or there is code missing (at the time of writing).
There are these issues:
The returned value from either recursive self.isSubTree calls is not used at all. This cannot be right, as certainly you need that information to conclude anything about the work done.
if not root: return will not return a boolean value when it kicks in. This should make the distinction between the case where also subTree is None: in that case the returned value should be True. Otherwise it should be False
Assuming your non-provided dfs code is correct, it will deal well with the previous point as well (where one or both of the arguments is None), and will compare the node values correctly, so then if not root should not be performed before dfs is called, as dfs will take care of that.
Similarly if root.val == subTree.val should not have to occur where it now occurs, but only in dfs.
Concluding, the working code could be like this:
class Solution(object):
def isSubtree(self, root, subRoot):
def dfs(root, subRoot):
if not root or not subRoot or root.val != subRoot.val:
return root == subRoot # Must both be None to have success
return dfs(root.left, subRoot.left) and dfs(root.right, subRoot.right)
return dfs(root, subRoot) or root and (
self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)
)
Solved it with the following:
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
def dfs(r1, r2):
# some function
if not root:
return False
if root.val == subRoot.val:
if dfs(root, subRoot):
return True
return self.isSubtree(root.left, subRoot) or self.isSubtree(root.right, subRoot)
For the purposes of this challenge, we define a binary tree to be a binary search tree with the following ordering requirements:
The value of every node in a node's left subtree is less than the data value of that node.
The value of every node in a node's right subtree is greater than the data value of that node.
Given the root node of a binary tree, can you determine if it's also a binary search tree?
Complete the function in your editor below, which has parameter: a pointer to the root of a binary tree. It must return a boolean denoting whether or not the binary tree is a binary search tree. You may have to write one or more helper functions to complete this challenge.
Input Format
You are not responsible for reading any input from stdin. Hidden code stubs will assemble a binary tree and pass its root node to your function as an argument.
Constraints:
0<=data<=10^4
Output Format
You are not responsible for printing any output to stdout. Your function must return true if the tree is a binary search tree; otherwise, it must return false. Hidden code stubs will print this result as a Yes or No answer on a new line.
My Code:
""" 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):
if root is None or (root.left is None and root.right is None):
return True
if root.left.data>=root.data or root.right.data<=root.data:
return False
check_binary_search_tree_(root.left)
check_binary_search_tree_(root.right)
return True
Why am I getting Wrong Answer?
The problem with your code is
first you didn't do :
return check_binary_search_tree_(root.left) and check_binary_search_tree_(root.right)
next even if you do that, you are forgetting to keep the root's value in mind while checking the BST property for left and right children. It could be that your left child is totally a good BST but it fails to be a BST when you consider its parent. Look at the below example:
6
4 7
2 8
The subtree rooted at 4 is a good BST but fails when you consider its root's value of 6.
The solution is then to check the proper range of values at each node i.e.
left_limit < root.data < right_limit
You could write your function as :
def check_binary_search_tree_(root, min = -math.inf, max = math.inf):
if root is None:
return True
if root.data > min and root.data < max:
return check_binary_search_tree_(root.left, min, root.data) and check_binary_search_tree_(root.right, root.data, max)
return False
check_binary_search_tree_(root.left)
check_binary_search_tree_(root.right)
You don't return a result of these recursive functions. Try to do:
return check_binary_search_tree_(root.left) and check_binary_search_tree_(root.right)
Full code example that works:
def check_binary_search_tree_(root):
def isValid(node, lower=float('-inf'), upper=float('inf')):
if not node:
return True
if node.data <= lower or node.data >= upper:
return False
return isValid(node.left, lower, node.data) and isValid(node.right, node.data, upper)
return isValid(root)
I'm trying to build a BST (binary search tree) with dict in python. I do not understand why my code is not adding nodes to the BST. I saw a similar post here:
How to implement a binary search tree in Python?
which looks the same as my code except declaring a node class, but I would like to know why my dict implementation fails (and hopefully improve my understanding of parameter passing with recursion in python).
keys = [10,9,2,5,3,7,101,18]
start = {'key': keys[-1], 'val': 1, 'left': None, 'right': None}
def binarySearch(root, node):
# compare keys and insert node into right place
if not root:
root = node
elif node['key'] < root['key']:
binarySearch(root['left'], node)
else:
binarySearch(root['right'], node)
# Now let's test our function and build a BST
while keys:
key = keys.pop()
node = {'key': key, 'val': 1, 'left': None, 'right': None}
binarySearch(start, node)
print(start) # unchanged, hence my confusion. Thx for your time!
===========================================
Edit: here is the code that would make it work!
def binarySearch(root, node):
# compare keys and insert node into right place
if not root:
root = node
elif node['key'] < root['key']:
if not root['left']: root['left'] = node
else: binarySearch(root['left'], node)
else:
if not root['right']: root['right'] = node
else: binarySearch(root['right'], node)
Here is what I think that is happening under the hood (why one version is able to add to BST but the other one is not):
In the original version, we will reach a recursion call where root still points to None inside the BST, but then root = node make root points to node which has absolutely no connection with start, i.e. the BST itself. Then local variables are deleted and no changes are made.
In the modified version, we will avoid this since when we add the node by e.g. root['left'] = node. Here root is still pointing to the original BST and thus we are modifying the key-val pair in the original BST instead of having root point to something totally outside the BST.
Let's run through your code as though we were the python interpreter.
Lets start at the first call: binarySearch(start, node)
Here start is the dict defined at the top of your script and node is another dict (which curiously has the same value).
Lets jump inside the call and we find ourselves at: if not root: where root refers to start above and so is truthy so fails this if.
Next we find ourselves at: elif node['key'] < root['key']: which in this case is not True.
Next we pass into the else: and we are at: binarySearch(root['right'], node).
Just before we jump into the first recursive call, lets review what the parameters to the call are: root['right'] from start has the value None and node is still the same dict which we want to insert somewhere. So, onto the recursive call.
Again we find ourselves at: if not root:
However this time root just refers to the first parameter of the first recursive call and we can see from the above review of the parameters that root refers to None.
Now None is considered falsy and so this time the if succeeds and we are on to the next line.
Now we are at root = node.
This is an assignment in python. What this means is that python will use the variable root to stop referring to None and to refer to whatever node currently refers to, which is the dict which was created in the while loop. So root (which is just a parameter, but you can think of as a local variable now) refers to a dict.
Now what happens is that we are at the end of the first recursive call and this function ends. Whenever a function ends, all the local variables are destroyed. That is root and node are destroyed. That is just these variables and not what they refer to.
Now we return to just after the first call site i.e. just after binarySearch(root['right'], node)
We can see here that the parameters: root['right'], node still refer to whatever they were referring to before. This is why your start is unchanged and why your program should deal with left and right now instead of recursing.
#Creted by The Misunderstood Genius
def add_root(e,key):
''''
e is node's name
key is the node's key search
'''
bst=dict()
bst[e]={'key':key,'P':None,'L':None,'R':None}
return bst
def root(tree):
for k,v in tree.items():
if v['P'] == None:
return k
def insert(tree, node, key):
tree[node]={'key':key,'P':None,'L':None,'R':None}
y =None
x = root(tree)
node_key = tree[node]['key']
while x is not None:
y=x
node_root=tree['R']['key']
if node_key < node_root:
x=tree[x]['L']
else:
x=tree[x]['R']
tree[node]['P']=y
if y is not None and node_key< tree[y]['key']:
tree[y]['L']=node
else:
tree[y]['R']=node
return tree
def print_all(tree):
for k,v in tree.items():
print(k,v)
print()
'''
Give a root node and key search target
Returns the name of the node with associated key
Else None
'''
def tree_search(tree,root, target):
if root ==None:
print(" key with node associate not found")
return root
if tree[root]['key'] == target:
return root
if target < tree[root]['key']:
return tree_search(tree,tree[root]['L'],target)
else:
return tree_search(tree,tree[root]['R'],target)
def tree_iterative_search(tree,root,target):
while root is not None and tree[root]['key']!=target:
if target < tree[root]['key']:
root=tree[root]['L']
else:
root=tree[root]['R']
return root
def minimum(tree,root):
while tree[root]['L'] is not None:
root=tree[root]['L']
return tree[root]['key']
bst=add_root('R',20)
bst=insert(bst,'M',10)
bst=insert(bst,'B',8)
bst=insert(bst,'C',24)
bst=insert(bst,'D',22)
bst=insert(bst,'E',25)
bst=insert(bst,'G',25)
print_all(bst)
print(tree_search(bst,'R',25))
x=tree_iterative_search(bst,'R',25)
print(x)
#print(minimum(bst,'R'))