二叉树23:树的子结构

来源:互联网 发布:mysql 自定义变量 编辑:程序博客网 时间:2024/05/17 07:21

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

思路:何为子结构,就是具有相同值的结点和结点关系的若干个结点组成的结构。基本思想是保持树B不动,先遍历树A,找到与树B相同的根结点,遍历树A可以使用先序遍历或者其他遍历方式都可以,只需要在遍历结点的代码中添加上判断是否相同的逻辑即可,如果遇到相同的结点node,此时进行判断,即判断在树node中是否包含B,这里可以将判断逻辑封装到一个函数DoesTree1HavaTree2(root1,root2)中,DoesTree1HavaTree2(root1,root2)里面的逻辑是判断在树root1中是否包含树root2,需要判断各个结点是否相同,然后对子节点再次判断新的子树root1中是否包含子树root2(不管root1和root2是否为null都要判断是否包含)。注意要判断的是root1中是否包含root2,而不是root1是否等于root2,先判断root1和root2结点是否满足包含的要求,然后对root1.left和root2.left以及root1.right和root2.right分别进行是否包含的判断。判断2个结点是否包含的逻辑很清晰:如果root2==null,那么不管root1是否==null,都应该是true;如果root1!=null,当root1==null时返回false,当root1!=null&&root2!=null时,判断root1.val与root2.val是否相同,如果不同则返回false,如果相同,则对它们的孩子分别进行包含性的判断,即DoesTree1HavaTree2(root1.left, root2.left)&&DoesTree1HavaTree2(root1.right, root2.right),如果都为true,则表示root1是真正包含root2的。

总结:本题首先对整棵树A进行先序遍历,在遍历代码区域添加if()语句块来判断是否遇到相同的结点,如果遇到相同的结点再进行进一步的判断逻辑,调用DoesTree1HavaTree2(root1,root2)方法判断root1中是否包含root2,这是一个递归方法,会遍历所有结点,直到root2遍历结束,何时结束?当root2==null时就return true。对root1和root2的左右子树分别进行DoesTree1HavaTree2判断,如果都为true则返回true即可。本题需要设计2个递归,一个遍历结点,一个判断2棵树是否具有包含的关系,即在递归里面套递归,但是原理较简单,按照逻辑来解决也不麻烦。

注意:DoesTree1HavaTree2(root1,root2)用来判断树root1是否包含树root2,且此时root1和root2是相同的,之所以在方法体代码中还要判断root1和root2是否相同是对于root1,root2下面的子树之间的包含关系时用的。而题目初始给定的pRoot1和pRoot2是不相同的,因此不能直接使用DoesTree1HavaTree2(pRoot1,pRoot2)来进行判断,要先遍历A找到A中与B相同根结点的结点再调用DoesTree1HavaTree2(root1,root2)进行包含性判断。

//给定2个树,判断树B是否是树A的一个子结构:递归遍历找相同根结点--递归判断树是否包含public class Solution {    boolean flag;    public boolean HasSubtree(TreeNode root1,TreeNode root2) {        //特殊输入:空树不是子结构        if(root2==null) return false;        //调用递归方法判断root2是否是root1的子结构        this.process(root1,root2);        return flag;    }        //先序遍历二叉树找到和root2相同的结点再进行树的包含性判断    private void process(TreeNode root1,TreeNode root2){        //遍历树A的边界条件        if(root1==null) return;        if(root1.val==root2.val){            //根结点相同,需要判断树是否包含            if(this.DoesTree1HavaTree2(root1,root2)){                //如果包含结束,并将flag设置为true,但是递归还是无法结束,必须全部结束                flag=true;                return;            }        }        //1:遍历树A的左右子树        if(!flag){         this.process(root1.left,root2);        this.process(root1.right,root2);        }    }    //递归方法:判断树root1是否包含树root2    private boolean DoesTree1HavaTree2(TreeNode root1,TreeNode root2){        if(root2==null) return true;        else if(root1==null) return false;        //此时root1和root2都不为null        //先判断root1和root2的值是否相同        if(root1.val!=root2.val) return false;        //此时root1==root2,继续判断左右孩子是否包含        boolean left=DoesTree1HavaTree2(root1.left,root2.left);        boolean right=DoesTree1HavaTree2(root1.right,root2.right);        return left&&right;    }}

F1注意,上面标记处,当flag为true时,说明此时已经找到了B的结构了,于是之后就不希望继续遍历了,如果不加if(!flag){}判断,那么之后的递归this.process(root1.left,root2)和this.process(root1.right,root2)还是会继续进行,即即使已经找到了B的结构,但是还是会将树A中的所有结点都遍历完,这样其实是浪费时间的,因此,添加一个对flag的判断,当flag=true时,因为B已经找到,于是不再遍历其他结点,该返回时就返回。当然不加也不会错,但是最好进行优化,面试时要透露出这个思考过程。

0 0
原创粉丝点击