剑指Offer_#26_树的子结构

剑指Offer_#26_树的子结构

Contents

题目

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

示例 1:

输入:A = [1,2,3], B = [3,1]
输出:false

示例 2:

输入:A = [3,4,5,1,2], B = [4,1]
输出:true

限制:

0 <= 节点个数 <= 10000

思路分析

题意分析

题意有一点模糊的地方,就是没讲清楚输入数组的形式代表的是哪种遍历的序列,经过测试,这个代表的是广度优先遍历的序列,也就是按照从上到下,从左到右的顺序进行遍历。

思路

主要分为两个步骤,
1.第一个步骤是找到A当中与B的根节点相同的节点
2.第二个步骤是从这个相同的节点出发,判断两个节点的子树的结构是否相同

两个步骤都可以通过递归进行前序遍历,分别分析两个递归代码。

步骤1:遍历A寻找相同节点

  • 出口条件:
    • A和B其中一个是null,返回false
      • A是null:说明将整个父树遍历完了,还没有找到与B相同的子结构
      • B是null:注意到递归过程中,B是null,说明输入的B就是null,根据题目中条件,空树不是任意一个树的子结构,直接返回false
    • A.val == B.val,说明找到了与A相同的节点,调用判断子树是否相同的函数进一步判断,如果结果为true,就返回true
  • 递推过程:
    • 继续判断从A的左子节点和右子节点出发,子树结构是否和B相同。返回二者的或。

步骤2:遍历AB判断子树结构是否相同

写法1:逆向思维,如果从来没有不相同的节点,所有节点都相同
  • 出口条件

    • B已经遍历完成,即遇到B == null,返回true (隐含的逻辑是:之前一直没有返回false,才可以到这一步,所以之前的所有对应节点都是相同的)
    • A已经遍历完成且B还没遍历完,子树结构不可能相同,返回false
    • A和B的值不同,子树结构不可能相同,返回false
  • 递推过程

    • 判断A的左右子树与B的左右子树结构是否相同。
写法2:正向思维,如果每个节点都相同,那么所有节点相同

与写法1的区别在于,这里边有一个“与”的逻辑,也就是说,每一次递归调用时,A与B的节点都相同,才可以保证所有节点都相同。
这样写是比较麻烦的,因为这意味着,需要单独维护一个全局变量isSubTreeSame,在每个递归过程中更新这个变量。
相比写法1修改的地方

  • 出口条件:
    • 当B遍历结束时,返回isSubTreeSame
  • 递推过程:
    • 更新变量isSubTreeSameisSubTreeSame = (isSubTreeSame && (A.val == B.val));

解答

解答1:逆向思维

class Solution {
    public boolean isSubStructure(TreeNode A, TreeNode B) {
        if(A == null || B == null) return false;
        if(A.val == B.val){//在A中寻找B的根节点,找到后就继续判断此根节点的子树是否相同
            if(isSubTreeSame(A,B)) return true;
        }
        return isSubStructure(A.left,B) || isSubStructure(A.right,B);
    }
    //输入两个根节点,判断子树结构是否相同
    public boolean isSubTreeSame(TreeNode A,TreeNode B){
        if(B == null) return true;//B树遍历完的时候,且从未出现节点值不相等的情况,就证明是相同的结构
        if(A == null && B != null) return false; 
        if(A.val != B.val) return false;//这个出口条件是反着定义的,因为如果判断true,需要多次比较都相同,是一个且的逻辑,不太好写
        return isSubTreeSame(A.left,B.left) && isSubTreeSame(A.right,B.right);
    }
}

解答2:正向思维

  1. class Solution { 
  2. boolean isSubTreeSame; 
  3. public boolean isSubStructure(TreeNode A, TreeNode B) { 
  4. if(A == null || B == null) return false; 
  5. if(A.val == B.val){//在A中寻找B的根节点,找到后就继续判断此根节点的子树是否相同 
  6. isSubTreeSame = true;//如果使用正的出口条件,需要在每次进入比较之前就对这个变量进行重置 
  7. if(isSubTreeSame(A,B)) return true; 
  8. } 
  9. return isSubStructure(A.left,B) || isSubStructure(A.right,B); 
  10. } 
  11. //输入两个根节点,判断子树结构是否相同 
  12. public boolean isSubTreeSame(TreeNode A,TreeNode B){ 
  13. if(B == null) return isSubTreeSame; 
  14. if(A == null) return false; 
  15. isSubTreeSame = (isSubTreeSame && (A.val == B.val)); 
  16. return isSubTreeSame(A.left,B.left) && isSubTreeSame(A.right,B.right); 
  17. } 
  18. } 

注意: 由于引入了额外的变量isSubTreeSame,需要在进入函数isSubTreeSame()之前,对变量进行重置(第6行),否则会影响到下一次调用的结果。

原文地址:https://www.cnblogs.com/Howfars/p/13220486.html