剑指Offer-22-包含min函数的栈

题目

定义栈的数据结构,请在该类型中实现一个能够得到栈最小元素的min函数。

解析

预备知识

栈是一种“后进先出”的线性表,我们在线性表的尾部进行添加和删除元素。很常见的生活中的例子,就是洗盘子。我们所用过得盘子,都是最后收拾的盘子在上面,所以第一个要洗的就是第一个盘子。还有火车进站出站等等。再举个程序员都知道的例子,就是我们函数调用栈的原型也是来源栈的数据结构。其中每一次函数调用和结束都对应一次入栈和出栈的过程。每一次函数调用称为调用栈的一个栈帧,它包括操作数栈、本地变量表、动态链接和返回地址。函数A调用函数B,函数B调用函数C,函数C运行完毕,继续执行函数B,最后是函数A。

思路一

大家可能跟我一样一开始的想法是申请一个变量min来存放当前最小值,每当入栈时都与该min比较,若比min小,则更新,否则什么都不做。可是这样有个很大的bug,那就是当数据栈中与该min值相等的元素弹出后,我们min这时就不是当前的最小值了,应该为当前数据栈的次最小值(即为次最小值)。可我们只记录最小值,没有记录次最小值啊,所以我们在压入这个最小元素之前,要保留当前的次最小值 。
我们需要一个数据结构来存放当前数据栈的最小值,这里可以选择数组,列表数据结构。为了和数据栈一样,我这里采用了一个栈来存放当前数据栈的最小值,每次需要获取数据栈的最小值的时候,访问最小值栈的栈顶即可。
我们规则为:
1. 当最小值栈为空时,则这时的值node肯定为当前的最小值,入栈即可。
2. 当node小于最小值栈顶时,则将node入栈最小值栈,否则复制最小值栈的值并再次入栈。我们目标使最小值栈元素始终与数据栈大小一致,当数据栈出栈时,最小值跟着出栈即可。这样代码实现简单,又能保证最小值栈的栈顶始终为当前数据栈的最小值。

在第二步骤中,我们如果只当node小于最小值栈顶时才将node入最小值栈的话,会导致当数据栈存在多个相同的最小值,当它们中任意一个弹出时,都会导致最小值栈跟着弹出,从而不能保证最小值栈的栈顶始终为最小值。

下面用图示看看:
入栈2, 因为最小值为空,所以2直接入最小值栈
这里写图片描述
入栈1,因为1小于栈顶,故把1入最小值栈
这里写图片描述
入栈4,因为4大于栈顶,故还是当前最小值栈的栈顶1入最小值栈
这里写图片描述
出栈,数据栈弹出4,同时最小值栈的栈顶1弹出。此时最小值栈的栈顶1还是为数据栈的最小值,说明我们思路是正确的。
这里写图片描述

    //数据栈---存数据
    static Stack<Integer> data = new Stack<>();
    //辅助栈---存最小值
    static Stack<Integer> minData = new Stack<>();

    public static void push(int node) {
        data.push(node);
        if(minData.isEmpty()) {
            minData.push(node);
        } else {
            minData.push(Math.min(node, minData.peek()));
        }
    }

    public static void pop() {
        if(data.size() == 0) {
            throw new RuntimeException("Stack is empty!");
        }
        data.pop();
        minData.pop();
    }

    public static int top() {
        return data.peek();
    }

    public static int min() {
        return minData.peek();
    }
优化

当前我们可以在node小于等于最小值栈的栈顶元素时才将node入栈,这样也能保证有多个相同的最小值也不会出错。

总结

多画图,多总结,没思路的时候,尝试用例子来解决!

猜你喜欢

转载自blog.csdn.net/dawn_after_dark/article/details/80869418