JZ26 树的子结构

28 篇文章 0 订阅

描述

输入两棵二叉树A,B,判断B是不是A的子结构。(我们约定空树不是任意一个树的子结构)

假如给定A为{8,8,7,9,2,#,#,#,#,4,7},B为{8,9,2},2个树的结构如下,可以看出B是A的子结构

数据范围:

0 <= A的节点个数 <= 10000

0 <= B的节点个数 <= 10000

示例1

输入:

{8,8,7,9,2,#,#,#,#,4,7},{8,9,2}

返回值:

true

示例2

输入:

{1,2,3,4,5},{2,4}

返回值:

true

示例3

输入:

{1,2,3},{3,1}

返回值:
false
好难,之前可能没遇到过 第一次做 完全不会

看了解析之后,发现难,难点在于:有两次递归
第一次递归仅仅只是遍历树1的结点而已
    找到与 树2根相同的节点时: 再单独将这两个节点作为根,再一起同步遍历判断是否一致

看完解析后,自己敲的代码:

递归代码:

   public boolean HasSubtree(TreeNode root1,TreeNode root2) {//只是遍历找匹配的根节点而已
        if(root2==null) return false;//空树不是任意一个树的子结构
        if(root1==null) return false;//这颗子树肯定没有 返回false
        if(root1.val==root2.val) {
            //找到与树2根相同得节点了 "摘下"以root1为根的子树开始同步遍历
            boolean f0=judge(root1,root2);
            if(f0) return true;//找到了直接返回 不递归了
        }

        boolean f1=HasSubtree(root1.left,root2);//大体结构 先序遍历root1 找与root2根相同的节点
        boolean f2=HasSubtree(root1.right,root2);//大体结构 先序遍历root1 找与root2根相同的节点  别忘记改成right

        return f1||f2;//左子树或者右子树找到匹配的均可
    }

    //真正核心 同步遍历了
    private boolean judge(TreeNode root1, TreeNode root2) {//此处初始root1 root2一定均不为null
        if(root2==null) return true;//root2遍历到null了  root1可以多点没事,正常结尾 这个分支不需要走了
        if(root1==null) return false;//此处root2肯定不为null root1却没了 必然不行
        if(root1.val!=root2.val){
            return false;//严格同步遍历到的值不等 肯定不行
        }
        //否则就是相等了 继续严格同步遍历下去即可
        boolean f1=judge(root1.left,root2.left);//完全严格同步地先序遍历了
        boolean f2=judge(root1.right,root2.right);//完全严格同步地先序遍历了

        return f1&&f2;//左右子树都重来没有返回false才行
    }

递归代码2:只是写好后再单纯地在语法层面进行了简化而已 (看起来又好厉害的亚子)

    public boolean HasSubtree(TreeNode root1,TreeNode root2) {//只是遍历找匹配的根节点而已
        if(root2==null||root1==null) return false;//空树不是任意一个树的子结构
        if(root1.val==root2.val && judge(root1,root2))  return true;
        return HasSubtree(root1.left,root2)||HasSubtree(root1.right,root2);//左子树或者右子树找到匹配的均可
    }
    //真正核心 同步遍历了
    private boolean judge(TreeNode root1, TreeNode root2) {//此处初始root1 root2一定均不为null
        if(root2==null) return true;//root2遍历到null了  root1可以多点没事,正常结尾 这个分支不需要走了  正常结尾返回true
        if(root1==null||root1.val!=root2.val) return false;//此处root2肯定不为null root1却没了 必然不行
        return judge(root1.left,root2.left)&&judge(root1.right,root2.right);//左右子树都重来没有返回false才行
    }

方法二:非递归解法 (代码长一点,但是没有递归,确实好理解一点了)

两次层序遍历
    第一次,寻找根节点相同的子树
    第二次:两树同步层次遍历: 此处其实很简单,以树2为基准判断是否将子节点add进队,树1完全相同操作且一直全部匹配即可


    java队列:
        add() 和 offer() 方法;二者都是向队列中添加元素
        当队列容量超过限制时,add()会报异常,offer()会返回false
   public boolean HasSubtree(TreeNode root1,TreeNode root2) {//只是遍历找匹配的根节点而已
        if(root2==null||root1==null) return false;
        Queue<TreeNode> q=new LinkedList<>();
        q.add(root1);
        while (!q.isEmpty()){//层序遍历 找与根相同的节点
            TreeNode p = q.poll();
            if(p.val==root2.val){
                if(judge(p,root2)){//看是否完全匹配  千万注意是 p和root2比较 p不要写成了root1
                    return true;
                }
            }
            if(p.left!=null) q.add(p.left);
            if(p.right!=null) q.add(p.right);
        }
        return false;//前面找到了匹配一定已经返回true了

    }
    //真正核心 同步遍历了
    private boolean judge(TreeNode root1, TreeNode root2) {//此处初始root1 root2一定均不为null
        Queue<TreeNode> q1=new LinkedList<>();
        Queue<TreeNode> q2=new LinkedList<>();
        q1.add(root1);q2.add(root2);
        while (!q2.isEmpty()){//以root2为基准 层序遍历root2 root1紧跟一样遍历 能完全匹配上就行
            TreeNode p1 = q1.poll();
            TreeNode p2 = q2.poll();
            if(p1==null) return false;//p2一定不为null p1却没了
            if(p1.val!=p2.val) return false;
            //接下来就是已经匹配了 得继续匹配其他节点了
            if(p2.left!=null){
                q1.add(p1.left);
                q2.add(p2.left);
            }
            if(p2.right!=null){
                q1.add(p1.right);
                q2.add(p2.right);
            }
        }
        return true;//中途没有返回false 说明完全匹配 返回true
    }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值