Loading problems...
TL;DR: The optimal pattern-based solution involves serializing both trees into strings (using Preorder traversal with null markers) and checking if the subRoot string is a substring of the root string.
In the LeetCode 572 problem, "Subtree of Another Tree," we are provided with two binary trees: a main tree root and a potential subtree subRoot. Our objective is to determine if subRoot exists exactly within root. This means there must be some node in root such that the tree rooted at that node is structurally identical to subRoot and shares identical node values.
The naive approach relies on standard recursion to traverse the main tree. For every node visited in root, we treat it as a potential candidate for the match. We launch a helper function (often called ) that compares the current subtree of against node by node.
isSameTreerootsubRootPseudo-code:
textfunction isSubtree(root, subRoot): if root is null: return false if isSameTree(root, subRoot): return true return isSubtree(root.left, subRoot) OR isSubtree(root.right, subRoot) function isSameTree(p, q): if p and q are null: return true if p or q is null or p.val != q.val: return false return isSameTree(p.left, q.left) AND isSameTree(p.right, q.right)
Complexity Analysis:
The time complexity of this approach is , where is the number of nodes in root and is the number of nodes in subRoot. In the worst-case scenario (e.g., a skewed tree with identical values), for every node in root, we might traverse all nodes in subRoot.
Why it is suboptimal:
While this approach passes the constraints of LeetCode 572 (), it performs redundant comparisons. We repeatedly traverse the same nodes in subRoot for different starting points in root. For significantly larger trees, this quadratic behavior would lead to performance issues.
The key intuition is that a Binary Tree can be uniquely represented by a string if we utilize a specific traversal order (like Preorder) and explicitly record null pointers.
If we serialize the root tree into a string and the subRoot tree into a string , the problem simplifies: Is a substring of ?
However, there are two crucial constraints we must enforce to make this work:
null children (e.g., using #) to distinguish between different structures that share the same values (e.g., a node with a left child vs. a node with a right child).12 in the string could falsely match a subRoot node with value 2. We wrap values (e.g., ^12) to ensure we match full numbers.Visual Description:
Imagine the recursion traversing the tree in Preorder (Root, Left, Right). As it visits node 3, it writes ^3. It moves left to 4, writes ^4. It hits a leaf's left child (null), writes #. By the end, the tree structure is "frozen" into a linear sequence of characters. If subRoot's sequence appears contiguously inside root's sequence, the structures match.

null, append a unique null marker (e.g., #) to the string.^ + val). Then, recursively serialize the left child, followed by the right child.S for root and string T for subRoot.T is a substring of S. If yes, return true; otherwise, false.Let's trace root = [3, 4, 5] and subRoot = [4].
Serialize subRoot ([4]):
4: Append ^4.#.#.T: ^4##Serialize root ([3, 4, 5]):
3: Append ^3.4):
4: Append ^4.#.#.5):
5: Append ^5.#.#.S: ^3^4##^5##Comparison:
^4## exists in ^3^4##^5##.true.A Preorder traversal that includes null markers provides a bijection between finite binary trees and their serialized strings. This means two trees are identical if and only if their serialized strings are identical. Consequently, if a tree is a subtree of , the serialized representation of must appear as a contiguous substring within the serialized representation of . The use of delimiters ensures that we do not match a suffix of a number (like 2 inside 12) or merge numbers incorrectly.
cppclass Solution { public: bool isSubtree(TreeNode* root, TreeNode* subRoot) { string fullTree = serialize(root); string subTree = serialize(subRoot); // Check if subTree string is a substring of fullTree string return fullTree.find(subTree) != string::npos; } private: string serialize(TreeNode* node) { if (!node) { return "#"; // Null marker } // Preorder: Root, Left, Right // Using '^' as a delimiter to distinguish values like 12 vs 1, 2 return "^" + to_string(node->val) + serialize(node->left) + serialize(node->right); } };
javaclass Solution { public boolean isSubtree(TreeNode root, TreeNode subRoot) { String fullTree = serialize(root); String subTree = serialize(subRoot); return fullTree.contains(subTree); } private String serialize(TreeNode node) { if (node == null) { return "#"; // Null marker } StringBuilder sb = new StringBuilder(); // Preorder: Root, Left, Right // Using '^' as a delimiter to separate values sb.append("^"); sb.append(node.val); sb.append(serialize(node.left)); sb.append(serialize(node.right)); return sb.toString(); } }
pythonclass Solution: def isSubtree(self, root: Optional[TreeNode], subRoot: Optional[TreeNode]) -> bool: def serialize(node): if not node: return "#" # Preorder: Root, Left, Right # Using '^' to distinguish values (e.g. ^12 vs ^1) return f"^{node.val}" + serialize(node.left) + serialize(node.right) full_tree_str = serialize(root) sub_tree_str = serialize(subRoot) return sub_tree_str in full_tree_str
root and for subRoot. The substring search (using efficient algorithms like KMP or standard library implementations which are typically optimized) runs in or close to it. This is an improvement over the worst-case of the brute force solution.The Tree - Serialization and Deserialization pattern used in LeetCode 572 is a powerful technique for converting tree structures into linear formats. This exact logic applies to:
Why does my solution fail on inputs like root=[12], subRoot=[2]?
[12] might look like ...12.... The string for [2] is 2. 2 is a substring of 12, leading to a false positive.^12 vs ^2.Why does my solution fail on structurally different trees with same values?
null nodes in the serialization.1 -> left: 2 produces Preorder 1, 2. A tree 1 -> right: 2 also produces Preorder 1, 2. They are indistinguishable without null markers.#) when a node is null. 1, 2, #, #, # vs 1, #, 2, #, #.Can I use Inorder Traversal for this?