树的子结构
解题思路:
-
若树 B 是树 A 的子结构,则子结构的根节点可能为树 A 的任意一个节点,因此,判断树 B 是否是树 A 的子结构,需完成以下两步工作:先序遍历树 A 中的每个节点 nA(对应函数 isSubStructure(A, B));再判断树 A 中 以 nA 为根节点的子树是否包含树 B(对应函数 dfs(A, B));
-
算法流程:
- dfs(A, B) 函数:
- 终止条件:
- 当节点 B 为空:说明树 B 已匹配完成,因此返回 true;
- 当节点 A 为空:说明已经越过树 A 叶子节点,即匹配失败,返回 false;
- 当节点 A 和 B 的值不同:说明匹配失败,返回 false;
- 返回值:
- 判断 A 和 B 的左子节点是否相等,即 recur(A.left, B.left) ;
- 判断 A 和 B 的右子节点是否相等,即 recur(A.right, B.right) ;
- 终止条件:
- isSubStructure(A, B) 函数:
- 特例处理: 当树 A 为空或树 B 为空时,直接返回 false;
- 返回值: 若树 B 是树 A 的子结构,则必满足以下三种情况之一,因此用或 || 连接;
- 以节点 A 为根节点的子树包含树 B,对应 dfs(A, B);
- 树 B 是树 A 左子树的子结构,对应 isSubStructure(A.left, B);
- 树 B 是树 A 右子树的子结构,对应 isSubStructure(A.right, B);
- dfs(A, B) 函数:
复杂度:
-
时间复杂度: O(MN);其中 M、N 分别为树 A 和树 B 的节点数量;先序遍历树 A 占用 O(M),每次调用 dfs(A, B) 判断占用 O(N);
-
空间复杂度: O(M);当树 A 和树 B 都退化为链表时,递归调用深度最大;当 M≤N 时,遍历树 A 与递归判断的总递归深度为 M ;当 M>N 时,最差情况为遍历至树 A 叶子节点,此时总递归深度为 M;
代码实现:
function isSubStructure(A: TreeNode | null, B: TreeNode | null): boolean {
if (A === null || B === null) return false;
function dfs(A, B) {
if (B == null) return true;
if (A == null) return false;
return A.val === B.val && dfs(A.left, B.left) && dfs(A.right, B.right);
}
return dfs(A, B) || isSubStructure(A.left, B) || isSubStructure(A.right, B);
};
剑指 Offer 28.对称的二叉树
上一篇