def is_ancestor(node, middle):
# search down
if node.data == middle.data:
return True
if node.left:
is_ancestor(node.left, middle)
if node.right:
is_ancestor(node.right, middle)
return False
I am using this function to recursively check is a node is an ancestor of middle.
Let's say we have a tree that looks like
5
/
2
\
4
and I say that node is the node that points to 5 and middle is 2.
When calling is_ancestor(node_with_5, node_with_2), I am expecting to recursively move the node down both to left and right and return True whenever it finds the middle.
However, my current function gives me False even though it will find the middle in the first recursion call.
Any help?
You would have to make some little changes à la:
def is_ancestor(node, middle):
if node is middle: # data could coincide, compare nodes directly
return True
if node.left and is_ancestor(node.left, middle):
return True # do actually return something
if node.right and is_ancestor(node.right, middle):
return True # do actually return something
return False
You could get the entire logic in an even more concise way:
def is_ancestor(node, middle):
if node is None:
return False
if node is middle:
return True
return is_ancestor(node.left, middle) or is_ancestor(node.right, middle)
Related
I have looked at this code line by line maybe 100 times and I am stumped. Why doesn't this work????
Problem: input of [5,4,6,null,null,3,7] (this is a BST with 5 being the root and 4 and 6 being its left and right nodes)
returns True when it should return False (3 should be not be to the right of parent node 5). False should be returned at the first nested if statement in the while loop.
def isValidBST(self, root: Optional[TreeNode]) -> bool:
if not root:
return True
# BFS method
current_node = root
queue = []
queue.append(current_node)
while len(queue) > 0:
current_node = queue.pop(0)
if current_node.left:
if (current_node.val > current_node.left.val) and (root.val > current_node.left.val):
queue.append(current_node.left)
else:
return False
if current_node.right:
if (current_node.val < current_node.right.val) and (root.val < current_node.right.val):
queue.append(current_node.right)
else:
return False
return True
The tree for which your code fails is:
5
/ \
4 6
/ \
3 7
When current_node is 6, your code checks that root.val > current_node.left.val, which means it checks that 5 > 3. This is true. And so it wrongly concludes there is no problem there, yet there is.
You may now think to just change the direction of that comparison, but there is a logical error in your approach: it is not enough to compare the root with every node in its subtree. For instance, in the following tree you should detect that 3 is violating the BST property, even though it is not in conflict with the root:
2
\
5
\
6
/
3
The conflict is with the value 5, which is neither the parent of 3, nor the root.
In conclusion, a BFS traversal is not really an ideal tool to verify a BST. Although it surely can be done, it is more natural to use DFS, as it allows to carry down the (accumulated) limitations that will apply to every node in the subtree.
So I have been trying out questions from Hackerrank and there has been a question on checking whether a tree is BST or not. I have been using the level order traversal for this check. if node.left.value> node.value and node.right.value<node.value the Tree must not be a BST.
Apart from the condition where I haven't checked for a single root node, all the other test cases fail. Where have I been going wrong?
def checkBST(root):
# if root.left and root.right is None:
# return True
if not root:
return
flag=0
q=[root]
while len(q)>0:
temp=q.pop(0)
if temp.left is not None:
if temp.left.data> temp.data:
flag=1
break
else:
q.append(temp.left)
if temp.right is not None:
if temp.right.data<temp.data:
flag=1
break
else:
q.append(temp.right)
if flag==1:
return False
else:
return True
It is not enough to test that a direct child is in the correct relation to its parent. There are invalid BSTs for which all those comparison-tests pass. You need to ensure that in the whole left subtree of a node there is no node with a greater value than that node's value.
So a level order traversal is not really the right tool to do this. It will work better with an in-order traversal, since a BST will have an non-decreasing in-order traversal.
I am trying to write a solution for Leet Code problem 261. Graph Valid Tree:
Given n nodes labeled from 0 to n-1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree.
Example 1:
Input: n = 5, and edges = [[0,1], [0,2], [0,3], [1,4]]
Output: true
Example 2:
Input: n = 5, and edges = [[0,1], [1,2], [2,3], [1,3], [1,4]]
Output: false
Here is my solution thus far. I believe that the goal is to detect cycles in the tree. I use dfs to do this.
class Node:
def __init__(self, val):
self.val = val
self.outgoing = []
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
visited = {}
for pre, end in edges:
if pre not in visited:
"we add a new node to the visited set"
visited[pre] = Node(pre)
if end not in visited:
visited[end] = Node(end)
"We append it to the list"
visited[pre].outgoing.append(visited[end])
def dfs(current, dvisit = set()):
if current.val in dvisit:
print("is the condition happening here")
return True
dvisit.add(current.val)
for nodes in current.outgoing:
dfs(nodes, dvisit)
return False
mdict = set()
for key in visited.keys():
mdict.clear()
if dfs(visited[key], mdict) == True:
return False
return True
It fails this test n = 5, edges = [[0,1],[1,2],[2,3],[1,3],[1,4]]
It is supposed to return false but it returns true.
I placed some print statements in my dfs helper function and it does seem to be hitting the case where dfs is supposed to return true. However for some reason, the case in my for loop does not hit in the end, which causes the entire problem to return true for some reason. Can I receive some guidance on how I can modify this?
A few issues:
The given graph is undirected, so edges should be added in both directions when the tree data structure is built. Without doing this, you might miss cycles.
Once edges are made undirected, the algorithm should not travel back along the edge it just came from. For this purpose keep track of the parent node that the traversal just came from.
In dfs the returned value from the recursive call is ignored. It should not: when the returned value indicates there is a cycle, the loop should be exited and the same indication should be returned to the caller.
The main loop should not clear mdict. In fact, if after the first call to dfs, that loop finds another node that has not been visited, then this means the graph is not a tree: in a tree every pair of nodes is connected. No second call of dfs needs to be made from the main code, which means the main code does not need a loop. It can just call dfs on any node and then check that all nodes were visited by that call.
The function could do a preliminary "sanity" check, since a tree always has one less edge than it has vertices. If that is not true, then there is no need to continue: it is not a tree.
One boundary check could be made: when n is 1, and thus there are no edges, then there is nothing to call dfs on. In that case we can just return True, as this is a valid boundary case.
So a correction could look like this:
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
if n != len(edges) + 1: # Quick sanity check
return False
if n == 1: # Boundary case
return True
visited = {}
for pre, end in edges:
if pre not in visited:
visited[pre] = Node(pre)
if end not in visited:
visited[end] = Node(end)
visited[pre].outgoing.append(visited[end])
visited[end].outgoing.append(visited[pre]) # It's undirected
def dfs(parent, current, dvisit):
if current.val in dvisit:
return True # Cycle detected!
dvisit.add(current.val)
for node in current.outgoing:
# Avoid going back along the same edge, and
# check the returned boolean!
if node is not parent and dfs(current, node, dvisit):
return True # Quit as soon as cycle is found
return False
mdict = set()
# Start in any node:
if dfs(None, visited[pre], mdict):
return False # Cycle detected!
# After one DFS traversal there should not be any node that has not been visited
return len(mdict) == n
A tree is a special undirected graph. It satisfies two properties
It is connected
It has no cycle.
No cycle can be expressed as NumberOfNodes ==NumberOfEdges+1.
Based on this, given edges:
1- Create the graph
2- then traverse the graph and store the nodes in a set
3- Finally check if two conditions above are met
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
from collections import defaultdict
graph = defaultdict(list)
for src, dest in edges:
graph[src].append(dest)
graph[dest].append(src)
visited = set()
def dfs(root):
visited.add(root)
for node in graph[root]:
if node in visited:
# if you already visited before, means you alredy run dfs so do not run dfs again
continue
dfs(node)
dfs(0)
# this shows we have no cycle and connected
return len(visited) == n and len(edges)+1 == n
This question is locked in leetcode but you can test it here for now:
https://www.lintcode.com/problem/178/description
I'm trying leetcode problem 572.
Given two non-empty binary trees s and t, check whether tree t has exactly the same structure and node values with a subtree of s. A subtree of s is a tree consists of a node in s and all of this node's descendants. The tree s could also be considered as a subtree of itself.
Since, tree's a great for recursion, I thought about splitting the cases up.
a) If the current tree s is not the subtree t, then recurse on the left and right parts of s if possible
b) If tree s is subtree t, then return True
c) if s is empty, then we've exhausted all the subtrees in s and should return False
def isSubtree(self, s: TreeNode, t: TreeNode) -> bool:
if not s:
return False
if s == t:
return True
else:
if s.left and s.right:
return any([self.isSubtree(s.left, t), self.isSubtree(s.right, t)])
elif s.left:
return self.isSubtree(s.left, t)
elif s.right:
return self.isSubtree(s.right, t)
else:
return False
However, this for some reason returns False even for the cases where they are obviously True
Ex:
My code here returns False, but it should be True. Any pointers on what to do?
This'll simply get through:
class Solution:
def isSubtree(self, a, b):
def sub(node):
return f'A{node.val}#{sub(node.left)}{sub(node.right)}' if node else 'Z'
return sub(b) in sub(a)
References
For additional details, you can see the Discussion Board. There are plenty of accepted solutions with a variety of languages and explanations, efficient algorithms, as well as asymptotic time/space complexity analysis1, 2 in there.
You need to change second if statement.
In order to check if tree t is subtree of tree s, each time a node in s matches the root of t, call check method which determines whether two subtrees are identical.
if s.val == t.val and check(s, t):
return True
A check method is look like this.
def check(self, s, t):
if s is None and t is None:
return True
if s is None or t is None or s.val != t.val:
return False
return self.check(s.left, t.left) and self.check(s.right, t.right)
While other code work well, your code will much simpler in else statement like the following. You don't need to check whether left and right nodes are None because first if statement will check that.
else:
return self.isSubtree(s.left, t) or self.isSubtree(s.right, t)
I wrote this algorithm for a coding challenge on HackerRank to determine if a give binary tree is a BST. Yet in some cases when the tree is not a BST my algorithm returns True anyway. I couldn't find what was wrong, everything seems ok, right? Or is there something I don't know about BSTs?
Node is defined as:
class node:
def __init__(self, data):
self.data = data
self.left = None
self.right = None
My algorithm is
def checkBST(root):
if not root:
return True
else:
if root.left and root.left.data >= root.data:
return False
if root.right and root.right.data <= root.data:
return False
return checkBST(root.left) and checkBST(root.right)
A binary search tree has all the nodes on the left branch less than the parent node, and all the nodes on the right branch greater than the parent node.
So your code fails on a case like this:
5
/ \
4 7
/ \
2 6
It's not a valid BST because if you searched for 6, you'd follow the right branch from the root, and subsequently fail to find it.
Take a look at a picture below for which your code is giving an incorrect answer:
Where are you going wrong:
1. if an element exists on the left subtree if there exists a node with a value greater than root.
2. if an element exists on the right subtree if there exists a node with a value smaller than root.
You should try this approach :
if not root:
return True
else:
if root.left and maximumOfSubtree(root.left) >= root.data:
return False
if root.right and minimumOfSubtree(root.right) <= root.data:
return False
return checkBST(root.left) and checkBST(root.right)
so the problem is to determine whether the given tree is a BST or not.
the best way to find out is through an inorder traversal.
Do In-Order Traversal of the given tree and store the result in a temp array.
Check if the temp array is sorted in ascending order, if it is, then the tree is BST.
this can be the approach
def check_binary_search_tree_(root):
visited = []
def traverse(node):
if node.left: traverse(node.left)
visited.append(node.data)
if node.right: traverse(node.right)
traverse(root)
fc = {}
for i in visited:
if i in fc:
return False
else:
fc[i]=1
m = sorted(visited)
if visited==m:
return True
return False
refer to this for other methods https://www.geeksforgeeks.org/a-program-to-check-if-a-binary-tree-is-bst-or-not/
method 1 and method 2 are similar to your approach so it will help you to understand that too.