[Leetcode] 255. Verify Preorder Sequence in Binary Search Tree

对于这一题,我们先来了解一下痛点或者说是规律是什么。
因为是Binary Search Tree,所以存在右子树大于左子树这个规律。然后preorder是基于左中右这个顺序来访问,也就是说,这个数组顺序如果出现“我走了左子树然后开始走右子树了,但又给我一个应该出现在左子树的值”的时候,verification就失败了。再翻译一下就是,“如果这个数组有一个a[n], 如果后来出现了一个比a[n]大的a[n+i]的值的时候,a[n + i]后面的数字a[n + i + 1].. a[n + i + 2]...都必须比a[n]大”。理由是这样的:因为是左中右,因为是BST,所以当你走完了a[n],之后出现的第一个比a[n]大的a[n + i]的时候,意味着你已经开始访问右子树的节点了(或者是某个祖先节点的右子树),因为右子树的节点都要比父亲节点大,所以a[n + i]之后的所有的数字都应该比a[n]要大。这个理论适用于整个数组的所有元素。

好了,我们知道该怎么fail掉这个verification了。然而我们该如何做呢。

O(n^2)的算法是很容易做的。先给个代码让大家体会一下,其实是能accept的。
 

    public boolean verifyPreorder(int[] preorder) {
        for (int i = 0; i < preorder.length; i++) {
            boolean largerMet = false;
            for (int j = i + 1; j < preorder.length; j++) {
                if (preorder[j] > preorder[i]) {
                    largerMet = true;
                } else if (largerMet) {
                    return false;
                }
            }
        }
        
        return true;
    }


但是这个,不科学。。我们先从O(n) time O(n) space的方法着手吧。
先看看5,2,1,3,4,  8,  6
1. 走到5,正常。(此时没有不能比之更小的数字)[5]
2. 走到2, 比5小,正常(此时也没有)[5, 2]
3. 走到1,比2小,也正常 (此时也没有)[5, 2, 1]
4. 走到3,比1也大,也比2大,所以不能再出现比2小的数字了。(此时为2)[5, 3]
5. 走到4,  比2大,同时也比3大, 所以不能再出现比3小的数字了(此时为3)[5, 4]
6. 走到8, 此时比4大,也比5大,所以不能出现比5小的数字了(此时为5)[8]
7. 走到6, 比5大但比8小,所以此时的底线还是5。(5)[8, 6]
 

在这个过程里,我们可以知道括号里面所描述的数字才是最重要的。也就是遍历数组的过程里,我们需要用当前遍历到的数字和括号里的数字做比较,同时维护更新括号里的数字。但再看看方框里的数字集合,你就会发现其实我们需要维护的是一个降序的集合。每扫到一个数字,就和这个集合从末到头进行比较,到第一个比它大的数字的时候(或者为空)就停下来。最后一个被比较到的数字将会是新的底线,也就是括号里的数字,而当前遍历到的数字会被push进这个集合里。所以可以知道,Stack会是比较好的达到这个目的的数据结构。

根据上述的描述,可以得到代码如下

    public boolean verifyPreorder(int[] preorder) {
        int curLow = Integer.MIN_VALUE;
        Stack<Integer> descendStk = new Stack<Integer>();
        for (int num : preorder) {
            if (num < curLow) return false;
            while (!descendStk.isEmpty() && num > descendStk.peek()) {
                curLow = descendStk.pop();
            }
            
            descendStk.push(num);
        }

        return true;
    }

这一题显而易见space是O(n)的,至于时间,因为每一个元素都会至多遍历一次,push一次和pop一次,所以其实也是O(n)。
好,我们再尝试一下,把Stack给略去。
其实整体流程差不多的,但你如果仔细阅读上面的流程,你就知道你是不能通过一两个index去完成这件事的,你是必须要放一个集合的。所以,我们只能借用input数组来做事情了。其实这个O(1)就是用一个index,和input array来模拟一个stack罢了。给出代码如下:

    public boolean verifyPreorder(int[] preorder) {
        int curLow = Integer.MIN_VALUE;
        int curIdx = -1;
        for (int num : preorder) {
            if (num < curLow) return false;
            while (curIdx >= 0 && num > preorder[curIdx]) {
                curLow = preorder[curIdx];
                curIdx--;
            }
            
            curIdx++;
            preorder[curIdx] = num;
        }

        return true;
    }

猜你喜欢

转载自blog.csdn.net/chaochen1407/article/details/81282892