Java算法体系学习(九)二叉树的递归套路(搜索二叉树的判断、满二叉树的判断、平衡二叉树的判断、Morris中序遍历)

七、二叉树的递归套路

  • 二叉树的递归套路就是在递归遍历过程中,对于每一个节点,看能够给左树和右树分别要到什么信息,然后加工出自己的信息,层层递归,最后解出问题。
  • 适用于只需要自己的左孩子和右孩子的信息就能加工出自己信息的问题,不必去深究左右子树的详细信息。
  • 套路,看需要什么信息,就建一个信息类,然后遍历每一个节点,遇到null时应该加入什么信息。然后底层向上层层层加工。

1、判断一个树是不是平衡二叉树

  • 平衡二叉树是一个空树,或者它的左右子树高度相差不超过1,并且左右子树都是平衡二叉树
  • 那么对于对个左右孩子,我们需要什么信息?
    1. 首先孩子是不是平衡二叉树,这是必须的,如果有一个不是就返回false
    2. 我们也需要左右子树的分别的高度,如果高度相差超过1那么也返回false
    3. DFS首先会遇到null节点,那么null节点的高度肯定为0,也是平衡二叉树,这也就是base case。
package tree.routine;

import tree.TreeNode;

public class Code09_IsBalanceTree {

    //我们所需要的信息集成在一个类中,分别需要高度和子树是否平衡的信息
    public static class Info{
        public int height;
        public boolean isBalanced;

        public Info(int height, boolean isBalanced) {
            this.height = height;
            this.isBalanced = isBalanced;
        }
    }

    public static boolean isBalanceTree(TreeNode root){
        return process(root).isBalanced;
    }

    public static Info process(TreeNode node){
        if (node == null){
            return new Info(0,true);
        }

        Info leftInfo = process(node.left);
        Info rightInfo = process(node.right);

        int height = 1 + Math.max(leftInfo.height,rightInfo.height);
        boolean isBalance = true;
        //如果左右任何子树不是平衡二叉树,那么以当前节点为根的树也不是平衡二叉树
        //两棵树高度相差超过1同样不是平衡二叉树,三者满足一个就不是平衡二叉树
        if (!leftInfo.isBalanced || !rightInfo.isBalanced ||
                Math.abs(leftInfo.height - rightInfo.height) > 1){
            isBalance = false;
        }

        return new Info(height,isBalance);
    }
    
}

  • 也可看到,这其实就是在后序遍历的基础上进行改进。先处理左树,再处理右树,最后处理自己。所以对于二叉树的问题,需要什么信息就直接定义内部类,要来信息直接加工处理即可,形成套路。

2、判断一个树是不是搜索二叉树

  • 对于空节点来说,它自己就是搜索二叉树。对于有存在孩子节点的节点来说,它左树的值均不大于它自己的值,它右树的值均不小于它自己的值。
  • 对于每个节点来说,中序遍历的顺序就是 左 —根 — 右,那么我们只需要判断中序遍历的结果是否升序排列就行。
  • 这里用两种方法,一种用中序遍历判断,一种用递归套路解决。
  • 对于递归套路,要判断某个节点是否为搜索二叉树,需要左子树的最大值,右子树的最大值,但是信息类里没有办法区分开来,那么直接定义子树的最大值和最小值,最后还需要子树是否为搜索二叉树。

2.1 非递归的中序遍历判断

  • 用非递归非递归,刚好复习前面二叉树的非递归中序遍历
    /**
     * 非递归的中序遍历实现
     * @param root
     * @return
     */
    public static boolean isSearchTreeByIn(TreeNode root){
        if (root == null){
            return true;
        }

        Stack<TreeNode> stack = new Stack<>();
        Integer pre = null;
        TreeNode cur = root;
        while (!stack.isEmpty() || cur != null){
            if (cur != null){
                stack.push(cur);
                cur = cur.left;
            }else {
                cur = stack.pop();
                if (pre != null && pre > cur.value){
                    return false;
                }
                pre = cur.value;
                cur = cur.right;
            }
        }
        return true;
    }

2.2 二叉树的递归套路

public static class Info{
    public boolean isSBT;
    public int max;
    public int min;

    public Info(boolean isSBT, int max, int min) {
        this.isSBT = isSBT;
        this.max = max;
        this.min = min;
    }
}

public static boolean isSBT(TreeNode root){
    if (root == null){
        return true;
    }
    return process(root).isSBT;
}

public static Info process(TreeNode node){
    if(node == null){
        return null;
    }

    Info leftInfo = process(node.left);
    Info rightInfo = process(node.right);

    //先把最小值最大值设置为当前节点的值,如果左右子树不为null则可以更新
    int max = node.value;
    int min = node.value;
    boolean isSBT = true;
    if (leftInfo != null){
        max = Math.max(max,leftInfo.max);
        min = Math.max(min,leftInfo.min);
        isSBT &= leftInfo.isSBT;
    }

    if(rightInfo != null){
        max = Math.max(max,rightInfo.max);
        min = Math.max(min,rightInfo.min);
        isSBT &= rightInfo.isSBT;
    }

    if (leftInfo != null && leftInfo.max >= node.value){
        isSBT &= false;
    }

    if (rightInfo != null && rightInfo.min <= node.value){
        isSBT &= false;
    }
    return new Info(isSBT,max,min);
}

2.3 Morris中序遍历

  • Morris遍历是二叉树的一种遍历方法,时间复杂度和递归非递归一样都是O(N),但是它将空间复杂度从O(M),(M为树的最大深度)降到了O(1)
  • Morris遍历的原理会在后面补上,因为稍微有点不好理解
    public static boolean isSBTByMorris(TreeNode root) {
        if (root == null) {
            return true;
        }

        boolean isSBT = true;
        Integer pre = null;
        TreeNode cur = root;
        //记录左子树的最右节点
        TreeNode mostRight = null;
        while (cur != null) {
            mostRight = cur.left;
            //有左子树
            if (mostRight != null) {
                //来到左子树的最后节点
                while (mostRight.right != null && mostRight.right != cur) {
                    mostRight = mostRight.right;
                }

                //第一次来到该节点
                if (mostRight.right == null) {
                    mostRight.right = cur;
                    cur = cur.left;
                    continue;
                } else {
                    //第二次来到该节点
                    mostRight.right = null;
                }
            }
            if (pre != null && pre > cur.value) {
                //注意不能直接返回,因为Morris遍历改变了二叉树的结构,必须等遍历完调整回来
                isSBT = false;
            }
            pre = cur.value;
            cur = cur.right;
        }
        return isSBT;
    }

3、判断一颗树是不是满树

  • 对于n层的二叉树来说,满树的节点有2^n - 1个
  • 所有我们需要两个信息,左右树的高度,左右树的节点个数。这里不需要左树和右树是不是满树的信息,因为可以根据高度和节点个数判断出来
package tree.routine;

import tree.TreeNode;

public class Code12_IsFullTree {
    public static class Info{
        public int height;
        public int nodesNum;

        public Info(int height, int nodesNum) {
            this.height = height;
            this.nodesNum = nodesNum;
        }
    }

    public static boolean isFullTree(TreeNode root){
        Info info = process(root);
        int height = info.height;
        int nodes = info.nodesNum;
        return (int) Math.pow(2,height) - 1 == nodes;
    }

    public static Info process(TreeNode node){
        if (node == null){
            return new Info(0,0);
        }

        Info leftInfo = process(node.left);
        Info rightInfo = process(node.right);
        int height = 1 + Math.max(leftInfo.height,rightInfo.height);
        int nodesNum = leftInfo.nodesNum + rightInfo.nodesNum + 1;
        return new Info(height,nodesNum);
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值