二叉树基础题(六):树的子结构&二叉搜索树的判断&镜像二叉树


(尊重劳动成果,转载请注明出处:https://yangwenqiang.blog.csdn.net/article/details/105928523
冷血之心的博客)

这段时间完成了职业生涯第一次跳槽,对算法题目有了一个更深的认识和理解,这里将二叉树常见的面试题目以及解法补充完善。

二叉树基础题(一):先序遍历&中序遍历&后序遍历

二叉树基础题(二):分层遍历&二叉树深度&是否相同的树

二叉树基础题(三):完全二叉树的判断&平衡二叉树的判断

二叉树基础题(四):对称二叉树的判断&之字形分层遍历二叉树

二叉树基础题(五):二叉树的下一个节点&最低公共祖先节点

二叉树基础题(六):树的子结构&二叉搜索树的判断&镜像二叉树


今天我们再来看三道二叉树相关的基础题目吧,如下所示:

  • 树的子结构
  • 二叉搜索树
  • 镜像二叉树

题目一:树的子结构

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

思路:
1、首先设置标志位result = false,因为一旦匹配成功result就设为true,剩下的代码不会执行,如果匹配不成功,默认返回false
2、递归思想,如果根节点相同则递归调用DoesTree1HaveTree2(),如果根节点不相同,则判断tree1的左子树和tree2是否相同,再判断右子树和tree2是否相同
3、注意null的条件,HasSubTree中,如果两棵树都不为空才进行判断,DoesTree1HasTree2中,如果Tree2为空,则说明第二棵树遍历完了,即匹配成功,tree1为空有两种情况:

  • 如果tree1为空&&tree2不为空说明不匹配,
  • 如果tree1为空,tree2为空,说明匹配。

代码实现如下:

public class Main {
    
    public boolean HashSubtree(TreeNode root1, TreeNode root2){
        boolean result = false;
         //当Tree1和Tree2都不为null的时候,才进行比较。否则直接返回false
        if(root1!=null&&root2!=null){
            //如果找到了对应Tree2的根节点的点
            if(root1.val==root2.val){
                 //以这个根节点为为起点判断是否包含Tree2
                result = DoesTree1haveTree2(root1,root2);
            }
             //如果找不到,那么就再去root的左儿子当作起点,去判断时候包含Tree2
            if(!result)
                result = HashSubtree(root1.left, root2);
            //如果还找不到,那么就再去root的右儿子当作起点,去判断时候包含Tree2
            if(!result)
                result = HashSubtree(root1.right, root2);
        }
        return result;
    }
    private boolean DoesTree1haveTree2(TreeNode root1, TreeNode root2) {
        //如果Tree2已经遍历完了都能对应的上,返回true
        if(root2==null)
            return true;
        //如果Tree2还没有遍历完,Tree1却遍历完了。返回false
        if(root1==null&&root2!=null)
            return false;
        //如果其中有一个点没有对应上,返回false
        if(root1.val!=root2.val)
            return false;
        //如果根节点对应的上,那么就分别去子节点里面匹配
        return DoesTree1haveTree2(root1.left, root2.left)
                &&DoesTree1haveTree2(root1.right, root2.right);
    }
}
class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;
    public TreeNode(int val) {
        this.val = val;
    }
}

题目二:是否是二叉搜索树

给定一个二叉树,判断其是否是一个有效的二叉搜索树。

二叉搜索树的定义如下:

  • 节点的左子树只包含小于当前节点的数。
  • 节点的右子树只包含大于当前节点的数。
  • 所有左子树和右子树自身必须也是二叉搜索树。

看到这里,也许大家很easy就可以写出如下的错误代码

public boolean isValidBST(TreeNode root) {
    if (root == null) 
    	return true;
    if (root.left != null && root.val <= root.left.val) 
    	return false;
    if (root.right != null && root.val >= root.right.val) 
    return false;

    return isValidBST(root.left) && isValidBST(root.right);
}

这个代码有问题吗?有吗?当然有问题了。我们的当前节点的值要大于左边子树的所有节点,而不仅仅是其一个左子树节点,右边也是同样的道理。比如下边所示并不是一个合法的二叉搜索树,但是我们的算法会返回true
二叉树基础题(六):树的子结构&二叉搜索树的判断&镜像二叉树_第1张图片

怎么办呢?

这个时候需要使用二叉树操作中经常需要使用的方法了,重载当前的函数,传入更多的参数,使得我们可以拥有更多的信息,最后才是递归调用!

正确的代码实现如下:

/**
* Definition for a binary tree node.
* public class TreeNode {
*     int val;
*     TreeNode left;
*     TreeNode right;
*     TreeNode(int x) { val = x; }
* }
*/
class Solution {
    public boolean isValidBST(TreeNode root) {
        return isValidBST(root,null,null);
    }
    // 使用辅助函数,增加函数参数列表,在参数中携带额外信息
    private boolean isValidBST(TreeNode root, TreeNode min,TreeNode max){
        if(root==null)
            return true;
        if(min!=null&&root.val<=min.val)
            return false;
        if(max!=null&&root.val>=max.val)
            return false;
            
        return isValidBST(root.left,min,root)&&isValidBST(root.right,root,max);
    }
}

题目三:镜像二叉树(二叉树的翻转)

二叉树的镜像:操作给定的二叉树,将其变换为原二叉树的镜像。

思路:

这道题目还算比较简单吧,其实也挺难的,Homebrew 的作者Max Howell面试被 Google
拒啦,因为他不会翻转二叉树。是不是很悲剧?
二叉树基础题(六):树的子结构&二叉搜索树的判断&镜像二叉树_第2张图片
这道题目可以给出两个解决方法:

  • 原地交换当前节点的左右节点
  • 新建一颗二叉树
class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;

    }

}

public class Solution {
    // Mirror求出的即是当前二叉树的镜像
    public void Mirror(TreeNode root) {
        if (root == null)
            return;
        // 交换当前节点的左右节点
        TreeNode tmpNode = root.left;
        root.left = root.right;
        root.right = tmpNode;

        Mirror(root.left);
        Mirror(root.right);
    }

    // 获取镜像二叉树(不改变原二叉树,重新生成了一个其镜像二叉树)
    private  TreeNode getMirror(TreeNode pRoot) {
        if (pRoot == null) {
            return null;
        }
        TreeNode root = new TreeNode(pRoot.val);
        root.right = getMirror(pRoot.left);
        root.left = getMirror(pRoot.right);
        return root;
    }
}

总结:

二叉树相关的题目的一个技巧就是重载一个函数用来递归,可以传入额外的参数,获取更多的信息。另外就是二叉树的递归解法中,我们只要定义一个递归函数,并且处理好当前节点要处理的事情即可,别的都交给递归函数,认为其已经给我们解决了问题,切莫在脑子里进行压栈和出栈的操作,因为我们的脑子都不够用哈~

就比如说二叉树的镜像算法中,我们只需要确定下递归结束条件,处理好当前节点的事情(交换),然后再分别递归左右子树即可。

后续我会继续更新二叉树相关基础题目,感兴趣的同学可以持续关注交流~


注意啦,注意啦~

欢迎大家关注我的牛客专栏《Java开发岗面试题全解析》 ,点击图片查看详情。

Java开发岗高频面试题全解析,专栏共计32节,已经全部更新完毕。

专栏分9个模块来对Java岗位面试中的知识点进行解析,包括通用面试技能,Java基础,Java进阶,网络协议,常见框架以及算法,设计模式等。

专栏串点成面的解析每个面试题背后的技术原理,由浅入深,循序渐进,力争让大家掌握面试题目的背后的技术原理,摒弃背题模式的陋习。


如果对你有帮助,记得点赞哈,欢迎大家关注我的博客,关注公众号(文强的技术小屋),学习更多技术知识,一起遨游知识海洋~

你可能感兴趣的:(面试笔试大全—二叉树)