二叉树子结构匹配指B的结构(含节点值)在A中以相同拓扑方式出现,不要求B占据A的完整子树;而子树匹配要求从某节点起整棵子树完全一致。

什么是二叉树子结构匹配,和子树有什么区别
子结构匹配 ≠ 子树匹配。子树要求从某个节点开始,**整棵子树完全一致**;而子结构只要求目标树的结构(含值)在原树中以相同拓扑方式出现,不要求该节点是叶子或完整子树——比如 target 可能只是原树某分支的“一部分”,其下方还可延伸其他节点。
典型场景:判断 B 是否为 A 的子结构,B 的根节点值必须在 A 中某处出现,且从该点出发,B 的每个非空节点都能在 A 对应位置匹配(B 的空节点不约束 A)。
核心逻辑:两层递归怎么写才不漏判
必须用两个函数配合:isSubStructure 负责在 A 中逐个尝试匹配起点;doesMatch(或类似名)负责从当前节点开始,严格校验 B 是否能“嵌入”进去。
-
isSubStructure(A, B):先判边界——若A或B为空,直接返回false(注意:B为空按题意不合法,不能算子结构);否则尝试三路:当前节点匹配 + 左子树递归 + 右子树递归,用||连接 -
doesMatch(A, B):这是关键。仅当B == nullptr时返回true(B已匹配完);若A == nullptr但B != nullptr,说明A提前结束,返回false;否则比较值,并递归检查左右子树
错误写法常见于把两层逻辑混在一个函数里,导致一旦某次匹配失败就终止整个搜索——漏掉其他可能的起点。
立即学习“C++免费学习笔记(深入)”;
边界条件和空指针怎么处理才安全
最容易崩的是对 nullptr 的判断顺序。必须保证访问 val 或子节点前,指针已非空。
- 在
doesMatch中,先判B == nullptr→ true;再判A == nullptr→ false;最后才用A->val == B->val -
isSubStructure开头必须判A == nullptr || B == nullptr,因为题目约定空树不是任何树的子结构 - 不要用
!A替代A == nullptr,虽等价但可读性差,易在复杂条件中出错
示例片段:
bool doesMatch(TreeNode* A, TreeNode* B) {
if (B == nullptr) return true;
if (A == nullptr) return false;
if (A->val != B->val) return false;
return doesMatch(A->left, B->left) && doesMatch(A->right, B->right);
}
为什么不能只用一次 DFS 就搞定
单次 DFS 会强制从 A 根开始匹配,但子结构可能出现在任意内部节点。比如 B 根值等于 A 的某个左孩子,但 A 根不匹配——此时单次遍历直接失败。
- 时间复杂度本质是 O(m×n),m 和 n 分别为两树节点数:最坏情况对
A每个节点都调用一次完整doesMatch - 空间复杂度取决于递归深度,最坏 O(max(height_A, height_B))
- 没有通用剪枝技巧,但可在
doesMatch前加快速值预检(如A->val != B->val)避免进入无谓递归
真正容易被忽略的点:匹配成功后不能提前全局 return,必须让外层 isSubStructure 的 || 逻辑自然短路——否则可能跳过更早的合法匹配点。


















