剑指 Offer 26. 树的子结构

输入两棵二叉树A和B,判断B是不是A的子结构。(约定空树不是任意一个树的子结构) B是A的子结构, 即 A中有出现和B相同的结构和节点值。

DFS+前序

思路

若B为A的子结构,则子结构的根节点可能为树A的任意一个节点,因此判断B是否为树A的子结构,需要完成如下2步工作:

  1. 先序遍历树A中每个节点26- ★★★★树的子结构(理解思路) - 图1;(isSubStructure(A,B)
  2. 判断以26- ★★★★树的子结构(理解思路) - 图2为根节点的子树是否包含树B。(recur(A,B));

    算法

    树A的根节点记作节点A,树B的根节点记作节点B

**recur(A,B)**:

  1. 终止条件:
    1. 当节点B为空,说明树B已匹配完成(越过叶子节点),true
    2. 当节点A为空:说明树A越过叶子节点,false
    3. 当节点A与B值不同:false;
  2. 返回值:
    1. 判断A和B的左子节点是否相等,即recur(A.left,B.left);
    2. 判断A和B的右子节点是否相等,即recur(A.right,B.right);

isSubStructure(A, B) 函数:

  1. 特例处理:当树A为空或树B为空时,false;
  2. 返回值:若树B为树A的子结构,则必然满足如下三种情况之一,以||连接
    1. 以节点A为根节点的子树包含树B,对应recur(A,B);
    2. 树B是树A的左子树的子结构,对应isSubStructure(A.left, B)
    3. 树B是树A的右子树的子结构,对应isSubStructure(A.right, B)

      以上a.b.c实质为先序遍历

实现(递归实现)

  1. bool recur(TreeNode*,TreeNode*);
  2. bool isSubStructure(TreeNode* A,TreeNode* B){
  3. return bool(A && B)
  4. && (recur(A,B) || isSubStructure(A->left,B) || isSubStructure(A->right,B);
  5. }
  6. bool recur(TreeNode* A,TreeNode* B){
  7. //用以判断以A为根节点的树是否包含树B
  8. if(!B) return true;
  9. if(!A || A->val!=B->val) return false;
  10. return recur(A.left,B.left) && recur(A.right,B.right);
  11. }

实现(非递归BFS实现)

  1. bool isSubStructure(TreeNode* A,TreeNode* B){
  2. if(!A || !B) return false;
  3. queue<TreeNode*> qu;
  4. qu.push(A);
  5. while(!qu.empty()){
  6. auto curNode=qu.front();
  7. qu.pop();
  8. if(bfs(curNode,B)){
  9. return true;
  10. }
  11. if(curNode->left) qu.push(curNode->left);
  12. if(curNode->right) qu.push(curNode->right);
  13. }
  14. return false;
  15. }
  16. bool bfs(TreeNode* A,TreeNode* B){
  17. queue<TreeNode*> quA,quB;
  18. quA.push(A);
  19. quB.push(B);
  20. while(!quA.empty() || !quB.empty()){
  21. auto curA=quA.front();
  22. auto curB=quB.front();
  23. quA.pop();
  24. quB.pop();
  25. if(!curB) continue;
  26. if(!curA || curA->val!=curB->val) return false;
  27. quA.push(curA->left);
  28. quA.push(curA->right);
  29. quB.push(curB->left);
  30. quB.push(curB->right);
  31. }
  32. return true;
  33. }

复杂度分析

  • 时间复杂度:26- ★★★★树的子结构(理解思路) - 图3 ,先序遍历A树为O(M),每次A树遍历都需要调用recur(A,B),占用O(N)
  • 空间复杂度:26- ★★★★树的子结构(理解思路) - 图4,当M<=N时,遍历树A与递归判断总深度为M;若M>N,最差情况下遍历至A的叶子节点,故总深度为M;

BFS

思路

算法

实现

复杂度分析

  • 时间复杂度:26- ★★★★树的子结构(理解思路) - 图5
  • 空间复杂度:26- ★★★★树的子结构(理解思路) - 图6

题目标题难度划分

星级 题目难度
简单
☆☆ 中等
☆☆☆ 困难

算法掌握难度程度划分

星级 掌握难度
普通
经典,易掌握
❤❤ 经典,略难掌握
❤❤❤ 困难