第3次面试:某米(2022-03-09)

  某米的面试,我前面的回答都正确了,这是第一次到了算法题环节,总共面试了55分钟,哦,耶,一个巨大的进步。

判断数组是否为二叉搜索树后序遍历的结果

  我用的方法是递归,但是递归肯定不是最佳答案。递归的思路很简单,对于数组先从右到左遍历,以最末尾元素为二叉树的root,然后向左找,一直找到比root小的元素。在这个位置把数组拆分为左子树和右子树。只需要校验左子树不存在比root大的元素就可以了。再继续以左子树为子数组,右子树为子数组进行递归判断。以下面这棵树为例子:
在这里插入图片描述
  后序遍历顺序为2, 4, 3, 6, 8, 7, 5。
  递归方案的代码是:

public class ArrayTest {

    public static void main(String[] args) {
        /*
         *   5
         * 3   7
         *2 4 6 8
         * 2 4 3 6 8 7 5
         */
            int[] array = {2, 4, 3, 6, 8, 7, 5};
        System.out.println(recursive(array, 0, array.length));
    }

    public static boolean recursive(int[] array, int start, int end) {
        // 递归结束点
        if (end - start <= 2) {
            return true;
        }

        final int root = array[end - 1];
        int i = end - 2;
        for (; i >= start; i--) {
            if (array[i] < root) {
                break;
            }
        }
        final int middle = i;
        for (; i >= start; i--) {
            if (array[i] >= root) {
                return false;
            }
        }
        return recursive(array, start, middle) && recursive(array, middle, end - 1);
    }
}

  但是有没有更好的方案呢?我们知道所有的递归都可以用栈来代替。而后序遍历用的也是双栈算法。把后序遍历的双栈反过来肯定不行,因为无法区分边界。所以需要再仔细分析需求,需求的最重要的就是检验右子树不能有比root小或左子树不能有比root大的。其实递归实际上也是用栈来实现,java中就是虚拟机栈。不过虚拟机栈,由于栈的每一层或者叫帧,保存了局部变量、方法引用等信息,所以开销比较大。如果用栈代替递归来实现,这时候的栈就是数据栈了,没一层只保存少量的数据,而这个栈使用的是堆内的内存,不用担心会有虚拟机栈溢出的风险。我们可以考虑用DFS来实现。如下图,就不是一棵二叉搜索树:
在这里插入图片描述
  这棵树的后序遍历顺序是2, 9, 3, 6, 8, 7, 5。首先我们确定右子树是6、8、7。因为左边两个都比root小,可以看成,都是左子树,都比root大可以看成右子树。判断的在于左子树不能出现比root大的元素,这种情况就是root、右子树、左子树点、某个比root大的元素。右子树可以没有,于是最小的不符合的情况就是类似这种,6,4,5。
  我们可以根据递归的本质,去构造递归树。以下是按数组索引构建的递归树:
在这里插入图片描述
根据这颗递归树,而这也恰恰是日常开发过程中一种最常见的用循环代替递归的方法,就是将参数作为栈的帧,来代替虚拟机的栈。也就是和虚拟机的栈保持一致,但是少了方法引用,并减少了公共参数。使用DFS写出的代码如下:

public class CheckPostRecursive {

    public static void main(String[] args) {
        int[] array = {2, 4, 3, 6, 8, 7, 5};
        System.out.println(check(array));;
    }

    private static boolean check(int[] array) {
        Deque<int[]> indices = new ArrayDeque<>(array.length);
        indices.add(new int[]{0, array.length-1});

        while (!indices.isEmpty()) {
            final int[] pop = indices.pop();
            final int end = pop[1];
            final int start = pop[0];
            int root = array[end];
            int leftChildEnd = end - 1;
            for (; leftChildEnd >= start; leftChildEnd--) {
                if (array[leftChildEnd] < root) {
                    break;
                }
            }
            for (int i = start; i < leftChildEnd; i++) {
                if (array[i] > root) {
                    return false;
                }
            }
            if (end > start) {
                indices.push(new int[]{start, leftChildEnd});
                indices.push(new int[]{leftChildEnd + 1, end - 1});
            }
        }
        return true;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

醒过来摸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值