Given the root node of a binary search tree (BST) and a value. You need to find the node in the BST that the node's value equals the given value. Return the subtree rooted with that node. If such node doesn't exist, you should return NULL.
this is what i get for my code:
class Solution:
def searchBST(self, root: TreeNode, val: int) -> TreeNode:
if root == None:
return None
elif root.val == val:
return root
elif root.val>val:
root = self.searchBST(root.left,val)
else:
root = self.searchBST(root.right,val)
which seems to output None.
but if replace the line
root = self.searchBST(root.left,val)
with
return self.searchBST(root.left,val)
same with root.right
then it works.
I am not too good at recursion but how can we do
return self.searchBST(root.left,val)
doesnt searchBST(..) return a TreeNode so dont we have to put the treenode in the variable root?
No as mentioned in question you need to return the node that matches the val.
In order to do that you have to return the address of the node if you find the value and None if does not.
Inorder to back propagate the value either the root address or Null(in case of not found) so you must return some address at every step.
If you are having problem with recursion go with the iterative traversal.
root iterativeSearch(struct Node* root, int key)
{
// Traverse untill root reaches to dead end
while (root != NULL) {
// pass right subtree as new tree
if (key > root->data)
root = root->right;
// pass left subtree as new tree
else if (key < root->data)
root = root->left;
else
return root; // if the key is found return 1
}
return Null;
}
Go to this for recursion visualisations:https://www.cs.usfca.edu/~galles/visualization/BST.html
You have to explicitly tell Python what you want the function to return. That's what you are doing with the return None and the return root lines.
The the left and right cases, you are only changing the local value of the parameter root, not changing anything outside of the scope of the function.
No explicit return is the same as return None.
Replacing the two root = by return should indeed do the work.
class Solution(object):
def is_leaf(self,root):
return root.left is None and root.right is None
def searchBST(self, root, val):
if root is None:
return root
if self.is_leaf(root):
if root.val == val:
return root
return None
if root.val == val:
return root
elif val < root.val:
return self.searchBST(root.left,val)
else:
return self.searchBST(root.right,val)
Related
I'm currently working on leetcode problem 366 where we have to find list of lists that contains values of leaves of each generation. I wanted to achieve this by recursion where if a node does not have left or right child, the value is recorded then the node removed by setting it to None. Here is my code:
def findLeaves(self, root: Optional[TreeNode]) -> List[List[int]]:
leaf_list = []
sub_list = []
def traverse(node):
if node == None:
return
if node.left == None and node.right == None:
sub_list.append(node.val)
node = None
return
traverse(node.left)
traverse(node.right)
return root
while True:
if root == None:
break
sub_list = []
traverse(root)
leaf_list.append(sub_list)
print(leaf_list)
return leaf_list
The problem seems to be that when a certain node is set to None, that change isn't retained. Why is it that I can't set a node to None to remove it?
Thanks
The tree can only be mutated when you assign to one if its node's attributes. An assignment to a variable, only changes what the variable represents. Such assignment never impacts whatever previous value that variable had. Assigning to a variable is like switching from one value to another without affecting any data structure. So you need to adapt your code such that the assignment of None is done to a left or right attribute.
The exception is for the root node itself. When the root is a leaf, then there is no parent to mutate. You will then just discard the tree and switch to an empty one (None).
One way to achieve this, is to use the return value of traverse to update the child-reference (left or right) that the caller of traverse needs to update.
Here is your code with those adaptations:
def findLeaves(root):
sub_list = []
def traverse(node):
if not node:
return
if not node.left and not node.right:
sub_list.append(node.val)
return # By returning None, the parent can remove it
node.left = traverse(node.left) # Assign the returned node reference
node.right = traverse(node.right)
return node # Return the node (parent does not need to remove it)
leaf_list = []
while root:
sub_list = []
root = traverse(root)
leaf_list.append(sub_list)
return leaf_list
Thanks for the corrections, I have made the amendments, but there is still one issue I can't get over:
class Solution:
def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
if not root:
return False
if not root.left and not root.right:
if root.val ==targetSum:
return True
else:
return False
remainingSum = targetSum - root.val
def dfs(remainingSum, root):
dfs(remainingSum - root.left.val, root.left)
dfs(remainingSum - root.right.val, root.right)
if remainingSum == 0:
return True
return dfs(remainingSum, root)
From within the recursive function, what do I return? Or is the code above correct now?
First, you are right about the two return statements:
return dfs(remainingSum,root)
return False
There is no way that second return statement could ever be executed. So let's look at the rest of the program. First, what should the logic be?
First, hasPathSum on entry checks to see if root evaluates to True and if not it return False. This is good.
It should then check to see if the root node's value is equal to the passed targetSum value because if it is, we can immediately return True. But instead your program is immediately subtracting the root node's value from targetSum yielding remainingSum and you never check for targetSum. So imagine a tree that consisted of only a root with no leaves whose value was 5 and we called hasPathSum with targetSum set to 5. We should be returning True. Remember: A leaf is a node with no children. Thus the root of this tree is also a leaf and should be checked.
Otherwise, recursively call hasPathSum on the left tree of the current node passing remainingSum. If the return value is True, then return True. (There is no need to first check to see if the left tree value exists with if root.left: because when you call hasPathSum recursively it is already checking if not root:)
Otherwise return the value you receive from calling hasPathSum on the right tree passing remainingSum.
There is no need for a separate dfs function.
If you just use the TreeNode constructor for creating and initializing tree nodes, then you will be creating your nodes "bottom up", i.e. leaves before their parents. For example:
node_1 = TreeNode(7)
node_2 = TreeNode(8)
node_3 = TreeNode(9, left=node_1, right=node_2)
node_4 = TreeNode(4, left=node_3)
node_5 = TreeNode(2, right=node_4)
node_6 = TreeNode(14)
root = TreeNode(6, left=node_5, right=node_6)
You should not return twice in a function.You should return the result of dfs.
Your dfs function with a wrong exit condition, you need check the remainingSum before run into next dfs.
When you find a leaf, you need to check the remainingSum, rather than return None.
code:
def hasPathSum(self, root: TreeNode, targetSum: int) -> bool:
def dfs(remainingSum,root):
if not root.left and not root.right:
if remainingSum == 0:
return True
else:
return False
if root.left:
x = dfs(remainingSum-root.left.val, root.left)
else:
x = False
if root.right:
y = dfs(remainingSum-root.right.val, root.right)
else:
y = False
return x or y
if not root:
return False
return dfs(targetSum - root.val,root)
result:
Runtime: 40 ms, faster than 83.76% of Python3 online submissions for Path Sum.
Memory Usage: 16 MB, less than 18.57% of Python3 online submissions for Path Sum.
I'm creating a basic binary search tree program where the nodes have to be strings however, I keep getting this error:
'builtins.AttributeError: 'NoneType' object has no attribute 'addNode'
I'm a bit confused because I thought you had to declare the children nodes as None. My code is below (please excuse the messiness and extra print statements):
class BinarySearchTree:
#constructor with insertion value & left/right nodes as None
def __init__(self, data):
self.data = data
self.left = None
self.right = None
#function to insert node
def addNode(root, data):
#when tree doesn't exist
if root == None:
return BinarySearchTree(data)
else:
#when node is already in tree
if root.data == data:
return root
#smaller values go left
elif data < root.data:
root.left.addNode(data)
#bigger values go right
else:
root.right.addNode(data)
return root
#function to find smallest node value (to help with deletion)
def smallestNode(root):
node = root
#loop goes to lowest left leaf
while(node.left is not None):
node = node.left
return node
#function to delete node
def removeNode(root, data):
if root == None:
return root
#when node to be deleted in smaller than root, go left
if data < root.data:
root.left = root.left.removeNode(data)
#when node to be deleted in bigger than root, go right
elif data > root.data:
root.right = root.right.removeNode(data)
##when node to be deleted in the same as root...
else:
#when node has only 1 or 0 children
if root.right == None:
move = root.left
root = None
return move
elif root.left == None:
move = root.right
root = None
return move
#when node has 2 children, copy then delete smallest node
move = root.right.smallestNode()
root.data = move.data
root.right = root.right.removeNode(move.data)
return root
def findNode(root, data):
#if current node is equal to data value then return the root
if root.data == data or root == None:
return root
#if current node is greater than the data value then, search to the left
elif data < root.data:
return root.left.findNode(data)
#if current node is less than the data value then, search to the right
else:
return root.right.findNode(data)
def createBST(keys):
root = BinarySearchTree(keys[0])
for i in range(1,len(keys)):
root.addNode(keys[i])
return root
print('Hi! Welcome to the Binary Search Tree Builder')
print('Here are your options below:')
print('1) Build new tree')
print('2) Add a node')
print('3) Find a node')
print('4) Delete a node')
choice = int(input('What would you like to do?: '))
if choice == 1:
nodes = list(input("Enter the strings you would like to build your tree from (separate by a space): ").split())
print(nodes)
tree = createBST(nodes)
print(tree)
I'm wondering where exactly is this error coming from and how can I fix it? Also, if you see any other problems occuring in my code, please let me know!
You have conflated the notion of "tree node" and "tree". What you have there is a mixture of the two. Remember, the first parameter to ALL member functions should be "self". Replacing "root" by "self" might make your problem more clear.
So, you might create a BinaryTree class, which has a single member called self.root that holds a BinaryTreeNode object, once you have a node. The nodes will hold the left and right values, each of which will either have None or a BinaryTreeNode object. The node class probably doesn't need much code -- just self.left and self.right.
The BinaryTree class will have an addNode member that knows how to traverse the tree to find the right spot, but when you find the spot, you just set self.left = BinaryTreeNode(...). A node does not know about the tree, so a node does not know how to add new nodes. That's a function of the tree itself.
Does that make your path forward more clear?
I am trying to implement the solution to the problem Lowest Common Ancestor(LCA) of Binary Tree via top-down recursion.
The approach I have used is:
IDEA: Find the node which has one of the desired nodes in either subtree while the other desired node is the opposite subtree.
PSEUDOCODE
1. If the value of root is equal to either of the desired node's value,
the root is the LCA.
2. Search in the left subtree for either node.
3. Search in the right subtree for either node.
4. If neither of them contains any of the two nodes,
the nodes do not exist in the tree rooted at the root node.
5. If each of them contains one node,
the root is the LCA.
6. If either one of them contains a node,
return it to the root.
This is what has also been recommended in answers of related questions on StackOverflow.
Now, this code works well if we assume all the nodes of the tree to be of unique value. In other words, this approach seems to break in case of duplicates (or, is it just my implementation?)
I would like to know how can I fix my code to work with duplicate values as well. If this approach cannot result in the optimal solution, how should I alter my approach?
Here is the exact implementation:
class TreeNode(object):
def __init__(self, x):
self.val = x
self.left = None
self.right = None
def __repr__(self):
return 'TreeNode({}, {}, {})'.format(self.val, self.left, self.right)
def lowestCommonAncestor(root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
if root is None:
return None
if p.val == root.val or q.val == root.val:
return root
left_subtree = lowestCommonAncestor(root.left, p, q)
right_subtree = lowestCommonAncestor(root.right, p, q)
if left_subtree is None and right_subtree is None:
return None
if left_subtree is not None and right_subtree is not None:
return root
if left_subtree is None:
return right_subtree
else:
return left_subtree
For example:
root = TreeNode(2)
root.left = TreeNode(3)
root.left.left = TreeNode(1)
root.right = TreeNode(5)
root.right.left = TreeNode(7)
root.right.right = TreeNode(1)
print(lowestCommonAncestor(root, TreeNode(1), TreeNode(7)))
This returns the root of the tree as the result.
result = TreeNode(2)
No doubt, the root is always an ancestor.
However, there exists a "lower" common ancestor than the root - TreeNode(5).
You have some problems:
1) Why are you checking for Node.val? You should be able to just compare one Node with another Node directly. That should solve issues when you have a tree with multiple nodes which have the same value... assuming that is your only problem.
2) Why do you return root if the left subtree is non-empty and the right subtree is non-empty? In many cases, of course, that will return root. This is generally not the behavior we want.
You may want to rethink your algorithm from scratch (you have some nice ideas going, but now that you see you've made some mistakes and since this problem is fairly simple/straightforward, it might be more beneficial to think afresh).
A suggestion: since this problem is a problem on trees, and has to do with search/paths, consider the problem of finding a path from Node p to Node q. We know that in a tree, there exists exactly one path from any two nodes (this simply follows from the fact that a tree is a connected acyclic graph, by definition).
You might keep this idea in mind when going back up after recursing to a base case, or after recursing to a base case you might want to create a data structure to store visited nodes in a loop then test some conditions and maybe recurse more (essentially a DFS approach vs a BFS approach, while the BFS approach uses explicit memoization / added memory, whereas DFS uses the stack to remember things).
The code will look like this
def lowestCommonAncestor(root, p, q):
"""
:type root: TreeNode
:type p: TreeNode
:type q: TreeNode
:rtype: TreeNode
"""
flag=0
if root is None:
return None
if p.val == root.val or q.val == root.val:
flag=1
#return root
left_subtree = lowestCommonAncestor(root.left, p, q)
right_subtree = lowestCommonAncestor(root.right, p, q)
if left_subtree is None and right_subtree is None:
if flag==0:
return None
else:
return root
if left_subtree is not None and right_subtree is not None:
return root
if left_subtree is None:
if flag==1:
if (right_subtree.value!=p && right_subtree.value!=q) || right_subtree.value==root.value:
return right_subtree
elif right_subtree.value!=root.value:
return root
else:
return right_subtree
else:
if flag==1:
if (left_subtree.value!=p && left_subtree.value!=q) || left_subtree.value==root.value:
return left_subtree
elif left_subtree.value!=root.value:
return root
else:
return left_subtree
The idea is like this: we recursively search p and q in root's left and right subtree. If the result of the left subtree is null, then it means p and q is not in root's left so we could safely conclude that the LCA must reside in root's right subtree. Similar conclusion holds true if the result of right subtree is null. But if neither of them is null, that indicates that p and q take either side of the root. Therefore, root is the LCA.
class Solution {
public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) {
if (root == null) {
return root;
}
if (root == p || root == q) {
return root;
}
TreeNode left = lowestCommonAncestor(root.left, p, q);
TreeNode right= lowestCommonAncestor(root.right, p, q);
if (left != null && right != null) {
return root;
} else if (left != null) {
return left;
} else {
return right;
}
}
}
var lowestCommonAncestor = function(root, p, q) {
const lca = (root) => {
// Check if the root is the value, is yes then just return the root
if (!root || root.val === p.val || root.val === q.val){
return root;
}
// Traverse through left and right tree
let L = lca(root.left);
let R = lca(root.right);
// If we have left and right node, which means val1 and val2 are on the either side of root,
// then return root else return L or R
if (L && R)
return root;
return L ? L : R;
}
return lca(root) || -1;
};
I have a practice interview question which tells me to verify if a tree is a balanced search tree or not and give a verification method... I have the class as
Class Node:
def __init__(self, k, val):
self.key = k
self.value = val
self.left = None
self.right = None
and other function definitions for the tree max and min values as
def tree_max(node):
maxleft = float('-inf') if not node.left else tree_max(node.left)
maxright = float('-inf') if not node.right else tree_max(node.right)
return max(node.value, maxleft, maxright)
def tree_min(node):
minleft = float('-inf') if not node.right else tree_min(node.left)
minright = float('-inf') if not node.left else tree_min(node.right)
return min(node.value, minleft, minright)
My verification method as
def verify(node):
if tree_max(node.left) <= node.value and node.value <= tree_min(node.right):
if verify(node.left) and verify(node.right):
return True
else:
return False
else:
return False
My problem occurs when I try to implement the verification method I seem to always get false even when I try to make a BST tree. My implementation is as follows:
root= Node(10, "Hello")
root.left = Node(15, "Fifteen")
root.right= Node(30, "Thirty")
print verify(root)
root = Node(10, "Ten")
root.right = Node(20, "Twenty")
root.left = Node(5, "Five")
root.left.right = Node(15, "Fifteen")
print verify(root)
Both are giving me False...Is there a problem with my verification function or my min/max function...Any help would be appreciated.
I see four errors in your code.
First, your check for null children is backwards in tree_min. That is, you're checking if node.right exists before accessing node.left, and vise versa.
Second, tree.min returns negative infinity when called on a leaf node. You need to use positive infinity in the min calculation (negative infinity is correct in the max version).
Third, you have a logic error within verify, as it unconditionally calls tree_min or tree_max and itself on it's child nodes, even if one or both of them are None. I suggest making all the functions handle being passed None, rather than relying on the caller to do the right thing. This also simplifies the min and max code a bit!
Lastly, you're doing your comparisons on node.value, which is the string you're giving each node. I suspect you want to be comparing using node.key instead. Comparing a float (like float("-inf")) to a string (like "ten") is an error in Python 3, and even in Python 2 where it is legal, it probably doesn't work like you would expect.
With those issues fixed, I get expected results when I create valid and invalid trees. Your two examples are both invalid though, so if you were using them to test, you will always get a False result.
Finally, a couple of minor style issues (that aren't bugs, but still things that could be improved). Python supports chained comparisons, so you can simplify your first if statement in verify to tree_max(node.left) <= node.key <= tree_min(node.right). You can further simplify that part of the code by connecting the checks with and rather than nesting an additional if statement.
Here's a version of your code that works for me (using Python 3, though I think it is all backwards compatible to Python 2):
class Node:
def __init__(self, k, val):
self.key = k
self.value = val
self.left = None
self.right = None
def tree_max(node):
if not node:
return float("-inf")
maxleft = tree_max(node.left)
maxright = tree_max(node.right)
return max(node.key, maxleft, maxright)
def tree_min(node):
if not node:
return float("inf")
minleft = tree_min(node.left)
minright = tree_min(node.right)
return min(node.key, minleft, minright)
def verify(node):
if not node:
return True
if (tree_max(node.left) <= node.key <= tree_min(node.right) and
verify(node.left) and verify(node.right)):
return True
else:
return False
root= Node(10, "Hello")
root.left = Node(5, "Five")
root.right= Node(30, "Thirty")
print(verify(root)) # prints True, since this tree is valid
root = Node(10, "Ten")
root.right = Node(20, "Twenty")
root.left = Node(5, "Five")
root.left.right = Node(15, "Fifteen")
print(verify(root)) # prints False, since 15 is to the left of 10