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.
Example 1:
root
= [3,4,5,1,2]
, subRoot
= [4,1,2]
Output: true
Example 2:
root
= [3,4,5,1,2,null,null,null,null,0]
, subRoot
= [4,1,2]
Output: false
Clarifications:
Can you provide an algorithm to solve this problem?
This problem requires us to determine if a binary tree subRoot
is a subtree of another binary tree root
. A subtree consists of a node in root
and all of its descendants.
The most straightforward approach is to traverse the tree root
and, for each node, check if the subtree rooted at that node is identical to subRoot
. This involves a recursive comparison.
root
tree using a depth-first search (DFS).root
, call a helper function to check if the subtree rooted at that node is identical to subRoot
.true
. Otherwise, continue traversing root
.root
completes without finding a match, return false
.class TreeNode:
def __init__(self, val=0, left=None, right=None):
self.val = val
self.left = left
self.right = right
class Solution:
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
if not subRoot:
return True
if not root:
return False
def is_identical(node1, node2):
if not node1 and not node2:
return True
if not node1 or not node2 or node1.val != node2.val:
return False
return is_identical(node1.left, node2.left) and is_identical(node1.right, node2.right)
def traverse(node):
if not node:
return False
if is_identical(node, subRoot):
return True
return traverse(node.left) or traverse(node.right)
return traverse(root)
root
and M is the number of nodes in subRoot
. In the worst case, we might have to compare subRoot
with every node in root
.root
tree. This is due to the recursive call stack during DFS.An optimized approach involves converting the trees into strings and then using string matching. The key idea is that if subRoot
is a subtree of root
, then the string representation of subRoot
should be a substring of the string representation of root
. To ensure uniqueness, we need to include null
markers in the string representation.
null
markers (e.g., "#") to represent empty nodes. A delimiter (e.g. comma) should also be used to seperate the nodes.root
and subRoot
into strings.subRoot
is a substring of the serialized string of root
.class Solution:
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
def serialize(node):
if not node:
return "#,"
return str(node.val) + "," + serialize(node.left) + serialize(node.right)
root_string = serialize(root)
subroot_string = serialize(subRoot)
return subroot_string in root_string
root
and M is the number of nodes in subRoot
. This is because we traverse both trees once to serialize them, and the substring check takes O(N) time in the worst case if using KMP algorithm.root
and subRoot
.subRoot
is None
, it's always a subtree (return True
).root
is None
, and subRoot
is not, subRoot
cannot be a subtree (return False
).By using the string serialization approach, we avoid repeated comparisons of subtrees, leading to a more efficient solution.