• 剑指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行),否则会影响到下一次调用的结果。

  • 相关阅读:
    我拒绝接受的几个最佳编程实践方法
    女人千万别写代码
    Visual Studio原生开发的10个调试技巧(二)
    20个很有用的PHP类库
    8个开发必备的PHP功能
    青少年如何使用 Python 开始游戏开发
    rmdir 删除空目录
    rm 删除文件或目录
    mv 移动或重命名文件
    cp 复制文件或目录
  • 原文地址:https://www.cnblogs.com/Howfars/p/13220486.html
Copyright © 2020-2023  润新知