栈6:最小栈和最大栈3道题

Posted 纵横千里,捭阖四方

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈6:最小栈和最大栈3道题相关的知识,希望对你有一定的参考价值。

 我们知道栈的特点是每次出栈只能是最后一入栈的那个,但是有些场景需要按照大小的顺序来出栈,这就要栈能够提供每次出栈能够出最大或者最小的那个。这个题目在很多材料里也都会出现,是栈的典型考题之一。我现在找到的有三道:

 

  1. 剑指 Offer 30. 包含min函数的栈

  2. LeetCode 155 最小栈

  3. LeetCode 716 最大栈

我们现在就来看一下怎么做。

1.剑指 Offer 30. 包含min函数的栈

这个题与LeetCode155基本一致, 最小栈题目要求:

定义栈的数据结构,请在该类型中实现一个能够得到栈的最小元素的 min 函数在该栈中,调用 min、push 及 pop 的时间复杂度都是 O(1)。

示例:

MinStack minStack = new MinStack();minStack.push(-2);minStack.push(0);minStack.push(-3);minStack.min();   --> 返回 -3.minStack.pop();minStack.top();      --> 返回 0.minStack.min();   --> 返回 -2.

普通栈的 push() 和 pop() 方法访问top元素的复杂度为 O(1) ;而如果要将 min() 函数复杂度也降为 O(1),一种方式是通过建立辅助栈实现;

  • 数据栈 AA :栈 AA 用于存储所有元素,保证入栈 push() 函数、出栈 pop() 函数、获取栈顶 top() 函数的正常逻辑。

  • 辅助栈 BB :栈 BB 中存储栈 AA 中所有 非严格降序 的元素,则栈 AA 中的最小元素始终对应栈 BB 的栈顶元素,即 min() 函数只需返回栈 BB 的栈顶元素即可。

因此,只需设法维护好 栈 BB 的元素,使其保持非严格降序,即可实现 min() 函数的 O(1)复杂度。图示:

因为要同时修改两个栈,我们必然要将普通的入栈和出栈方法都作一些改造:

函数设计:

  一.push(x) 函数:重点为保持栈 BB 的元素是 非严格降序 的。

  1. 将 xx 压入栈 AA (即 AA.add(x) );

  2. 若 ① 栈 BB 为空 或 ② xx 小于等于 栈 BB 的栈顶元素,则将 xx 压入栈 BB (即 B.add(x) )。

二.pop() 函数:重点为保持栈 AA, BB的 元素一致性 。

  1. 执行栈 AA 出栈(即 AA.pop() ),将出栈元素记为 yy ;

  2. 若 yy 等于栈 BB 的栈顶元素,则执行栈 BB 出栈(即 BB.pop() )。

三.top() 函数:直接返回栈 AA 的栈顶元素即可,即返回 AA.peek() 。

四.min() 函数:直接返回栈 BB 的栈顶元素即可,即返回 BB.peek() 。

 代码如下:

class MinStack {    Stack<Integer> A, B;    public MinStack() {        A = new Stack<>();        B = new Stack<>();    }    public void push(int x) {        A.add(x);        if(B.empty() || B.peek() >= x)            B.add(x);    }    public void pop() {        if(A.pop().equals(B.peek()))            B.pop();    }    public int top() {        return A.peek();    }    public int min() {        return B.peek();    }}

二.LeetCode716 最大栈

有最小就有最大,这个题目就是前面的最小换成最大。看要求:

设计一个最大栈,支持 push、pop、top、peekMax 和 popMax 操作。

push(x) – 将元素 x 压入栈中。

pop() – 移除栈顶元素并返回这个值。

top() – 返回栈顶元素。

peekMax() – 返回栈中最大元素。

popMax() – 返回栈中最大的元素,并将其删除。如果有多个最大元素,只要删除最靠近栈顶的那个。

这个方法和前面的基本类似,少废话,直接看代码:

class MaxStack {    // 所有值       Stack <Integer> stack  =  new Stack<Integer>();    // 当前的最大值    Stack <Integer> maxStack  =  new Stack<Integer>();    public void push(int x) {        // 保证stack和maxStack的元素数量一致, 即便 x == maxStack.peek(), 也会同时push到maxStack和stack        if (maxStack.isEmpty() || x >= maxStack.peek()){                maxStack.push(x);        }        stack.push(x);    }    public int pop() {       if (stack.peek().equals(maxStack.peek())){                   maxStack.pop();       }       return stack.pop();    }    public int top() {        return stack.peek();    }    public int peekMax() {        return maxStack.peek();    }    public int popMax() {         Stack <Integer> tempStack  =  new Stack<Integer>();            int max  = maxStack.peek();            while (!stack.peek().equals(maxStack.peek())){                 tempStack.push(stack.pop());            }            stack.pop();            maxStack.pop();            while(!tempStack.isEmpty()){               push(tempStack.pop());            }            return max;    }}

以上是关于栈6:最小栈和最大栈3道题的主要内容,如果未能解决你的问题,请参考以下文章

数据结构栈和队列,看完这一篇就够了(万字配动图配习题)

栈和队列的常见题型

codevs1170 双栈排序

Leetcode Practice --- 栈和队列

poj2796(单调栈+树状数组)

栈和队列