Last Updated: March 29, 2026
We have two binary trees. We need to figure out whether subRoot appears somewhere inside root as a complete subtree. "Complete subtree" is the important phrase here. It's not enough for subRoot to match just a portion of some subtree in root. The matching subtree must include all descendants, meaning the leaf nodes must also match up.
So if root has a node with value 4 that has children 1 and 2, and subRoot is exactly that same structure (4 with children 1 and 2, both being leaves), then we have a match. But if that node 2 in root has an extra child that subRoot doesn't have, it's not a match.
The key observation is that this problem breaks down into two sub-tasks: (1) find nodes in root that could be the "starting point" of a match, and (2) verify whether the subtree rooted at that node is structurally identical to subRoot.
1 <= number of nodes in root <= 2000 — The root tree is small. Even O(n^2) approaches will comfortably fit within time limits.1 <= number of nodes in subRoot <= 1000 — The subtree to match can be up to half the size of the main tree.-10^4 <= node values <= 10^4 — Integer values, no overflow concerns. Values can be negative, so we need to handle that in serialization approaches.The most natural way to think about this is: visit every node in root, and at each node, ask "does the subtree rooted here look exactly like subRoot?" If we find any such node, we return true. If we exhaust the entire tree without finding a match, we return false.
Checking whether two trees are identical is a classic recursive problem on its own. Two trees are the same if their root values match, their left subtrees are the same, and their right subtrees are the same. Both being null also counts as "the same."
So the solution is really two recursive functions layered on top of each other: one that traverses root looking for potential match points, and another that verifies a match by comparing two trees node by node.
root is null, return false (there's no subtree to match against).root is identical to subRoot using a helper function.subRoot is a subtree of root.left or root.right.isSameTree compares two trees: if both are null, return true. If one is null or values differ, return false. Otherwise, recursively compare left and right children.The brute force works but does redundant comparisons at every node. What if we represented each tree as a string and reduced the problem to substring matching?
Here's a different angle: what if we convert both trees into strings and then check if one string is a substring of the other? If we serialize both trees using preorder traversal, including markers for null nodes, then subRoot is a subtree of root if and only if the serialized subRoot appears as a substring within the serialized root.
The trick is in the serialization. We can't just concatenate values because that creates ambiguity. For example, a node with value "12" would look the same as a node "1" followed by "2". So we need delimiters between values and special markers for null children to preserve the tree structure.
Preorder traversal is particularly well-suited here because each subtree gets serialized as a contiguous block in the string. The subtree rooted at node X becomes a contiguous substring in the preorder serialization. So a substring match directly corresponds to a subtree match. By including null markers, we distinguish between trees that share the same values but have different shapes.
root into a string using preorder traversal. For each node, append a delimiter followed by the node's value. For null nodes, append a delimiter followed by a null marker (e.g., #).subRoot the same way.subRoot is a substring of the serialized root.The serialization approach works well but builds large strings. What if we could fingerprint each subtree with a hash and compare in O(1) per node, without building any strings at all?
This approach borrows an idea from Merkle trees (used in Git, blockchains, and file systems). The concept is simple: assign each subtree a hash value computed from its structure. A leaf node's hash depends on its value. An internal node's hash depends on its value combined with the hashes of its left and right children. If two subtrees have the same structure and values, they'll produce the same hash.
So we compute the hash for every subtree in root and the hash for subRoot. Then we just check if subRoot's hash appears anywhere in root. Each hash computation is O(1) per node, so the total work is O(m + n).
The one subtlety is hash collisions. Two different trees could theoretically produce the same hash. To handle this, when we find a hash match, we do a final isSameTree check to confirm.
subRoot by recursively hashing its tree structure.root and compute the hash of every subtree.root, check if the subtree hash matches subRoot's hash.isSameTree check to rule out hash collisions.