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:
Input: root = [1,2,3,4,5], subRoot = [2,4,5]
Output: trueExample 2:
Input: root = [1,2,3,4,5,null,null,6], subRoot = [2,4,5]
Output: falseConstraints:
1 <= The number of nodes in both trees <= 100.-100 <= root.val, subRoot.val <= 100
You should aim for a solution as good or better than O(m * n) time and O(m + n) space, where n and m are the number of nodes in root and subRoot, respectively.
A subtree of a tree is a tree rooted at a specific node. We need to check whether the given subRoot is identical to any of the subtrees of root. Can you think of a recursive way to check this? Maybe you can leverage the idea of solving a problem where two trees are given, and you need to check whether they are identical in structure and values.
When two trees are identical, it means that every node in both trees has the same value and structure. We can use the Depth First Search (DFS) algorithm to solve the problem. How do you implement this?
We traverse the given root, and at each node, we check if the subtree rooted at that node is identical to the given subRoot. We use a helper function, sameTree(root1, root2), to determine whether the two trees passed to it are identical in both structure and values.
Before attempting this problem, you should be comfortable with:
To check whether one tree is a subtree of another, we do two things:
root) using DFS.subRoot.So for every node in the big tree:
subRoot's root, we compare both subtrees fully.subRoot is a subtree.The helper sameTree simply checks whether two trees match exactly, node-for-node.
subRoot is empty → return true (empty tree is always a subtree).root is empty but subRoot is not → return false.root node:sameTree(root, subRoot) is true, return true.isSubtree(root.left, subRoot)isSubtree(root.right, subRoot)true if either side returns true.sameTree(root1, root2):
null → return true.null → return false.false.# Definition for a binary tree node.
# 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
if self.sameTree(root, subRoot):
return True
return (self.isSubtree(root.left, subRoot) or
self.isSubtree(root.right, subRoot))
def sameTree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
if not root and not subRoot:
return True
if root and subRoot and root.val == subRoot.val:
return (self.sameTree(root.left, subRoot.left) and
self.sameTree(root.right, subRoot.right))
return FalseWhere is the number of nodes in and is the number of nodes in .
Instead of comparing trees directly, we can first turn each tree into a string and then just check whether one string is contained in the other.
root and subRoot into strings using the same traversal (for example, preorder).null children (like #) and separators (like $) so different shapes don't accidentally look the same in the string.S_root = serialization of the main treeS_sub = serialization of the subtreeS_sub a substring of S_root?"To efficiently check this, we can use a linear-time pattern matching algorithm (like Z-function or KMP) instead of naive substring search.
Serialize a tree
$) + node value.null child, append a special marker (e.g., #$ or just #).Build strings
S_sub = serialization of subRoot.S_root = serialization of root.Combine for pattern matching
combined = S_sub + "|" + S_root| is just a separator that does not appear in the serializations.)Run pattern matching
combined.m = length(S_sub).Z[i] == m, then S_sub appears completely starting at that position → subRoot is a subtree → return true.If no such position is found, return false.
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def serialize(self, root: Optional[TreeNode]) -> str:
if root == None:
return "$#"
return ("$" + str(root.val) + self.serialize(root.left) + self.serialize(root.right))
def z_function(self, s: str) -> list:
z = [0] * len(s)
l, r, n = 0, 0, len(s)
for i in range(1, n):
if i <= r:
z[i] = min(r - i + 1, z[i - l])
while i + z[i] < n and s[z[i]] == s[i + z[i]]:
z[i] += 1
if i + z[i] - 1 > r:
l, r = i, i + z[i] - 1
return z
def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool:
serialized_root = self.serialize(root)
serialized_subRoot = self.serialize(subRoot)
combined = serialized_subRoot + "|" + serialized_root
z_values = self.z_function(combined)
sub_len = len(serialized_subRoot)
for i in range(sub_len + 1, len(combined)):
if z_values[i] == sub_len:
return True
return FalseWhere is the number of nodes in and is the number of nodes in .
A subtree must match exactly from a node down to all its leaves. A common mistake is checking only if the values match without verifying that the entire structure below also matches. If subRoot has children, those children must also exist and match in the main tree. Simply finding a node with the same value is not sufficient.
When comparing two trees for equality, both trees must have null children in the same positions. A frequent error is returning true when one node is null and the other is not, or failing to check both left and right subtrees. The base case must ensure that both nodes being compared are either both null (return true) or both non-null with matching values before recursing.