栈和队列系列1-最小栈/最大队列

316 阅读2分钟

一、数据结构

- 栈

特点:先进后出,后进先出;只允许在一端插入和删除数据;

时间复杂度:入栈和出栈的时间复杂度为O(1);

常见应用:函数调用栈、表达式求值、括号匹配等。

image.png

- 队列

特点:先进先出;入队:放一个元素到队列尾部;出队:从队列头部取出一个元素;

时间复杂度:入队和出队的时间复杂度为O(1)

常见应用:

  • 阻塞队列

    队列为空的时候,从队头取数据会被阻塞,直到队列中有了数据才能返回;如果队列已经满了,那么插入数据的操作就会被阻塞,直到队列中有空闲位置后再插入数据,然后再返回。

  • 并发队列

    同一时刻仅允许一个存或者取操作。

image.png

二、题目

1、队列的最大值

image.png 思路分析:

需要实现一个时间复杂度是O(1)的入队、出队函数,可以采用双端队列;还需要实现一个时间复杂度为O(1)的max_value()函数,也就是需要另外记录队列中的最大元素,记为MAX1,为了防止MAX1出队后最大元素的信息丢失,所以还需记录次大元素MAX2,以此类推,MAX1,MAX2...MAXN,可以另外维护一个单调递减的队列

如下图,记数据队列为qdata,辅助队列为qmax:

maxQueue.drawio.png

这里也许会有个问题:遇到入队元素value>qmax.peekLast(),就从队尾删除元素直到不满足条件,会不会导致一些次大元素被删除?

比如,当入队元素为3的时候,就要从qmax中删除元素2、2,会不会在这之后需要获取2作为max_value()函数的返回值? 答案是不会。因为把3入队之后,如果3作为max_value,然后被删除了,并不需要队列前面的2作为次大元素,因为在3被出队之前,2已经出队了

理清思路之后,就可以实现代码了。

class MaxQueue {
    // 数据队列
    Deque<Integer> qdata ;
    // 辅助队列
    Deque<Integer> qmax ;
    public MaxQueue() {
        qdata = new LinkedList<>();
        qmax = new LinkedList<>();
    }
    
    // 从辅助队列的队列头取元素返回
    public int max_value() {
        if(qmax.isEmpty())
            return -1;
        else return qmax.peekFirst();
    }
    
    // 更新数据队列和辅助队列
    public void push_back(int value) {
        qdata.addLast(value);
        // 维持一个单调栈
        while(!qmax.isEmpty()&&qmax.peekLast()<value){
            qmax.removeLast();
        }
        qmax.addLast(value);
        
    }
    
    public int pop_front() {
        if(qdata.isEmpty())
            return -1;
        // 如果当前出队的元素在辅助队列中,辅助队列的队列头元素也删除
        if(!qmax.isEmpty()&&qdata.peekFirst().equals(qmax.peekFirst())){
            qmax.removeFirst();
        }
        // 删除并返回数据队列的队列头元素 
        int value = qdata.peekFirst();
        qdata.removeFirst();
        return value;

    }
}

2、包含最小值的栈

image.png

思路分析:

实现思路和上一题大同小异,有两点不同:1、使用的数据结构是栈;2、维护的辅助栈是单调递增的;

代码实现:

class MinStack {

    // 需要借助一个辅助栈记录每次产生的最小元素
    Stack<Integer> s_data;
    Stack<Integer> s_min;
    /** initialize your data structure here. */
    public MinStack() {
        s_data = new Stack<>();
        s_min = new Stack<>();
    }
    
    public void push(int x) {
        if(s_min.isEmpty()||x<=s_min.peek()){
            s_min.push(x);
        }
        s_data.push(x);
    }
    
    public void pop() {
        // 弹出的元素在辅助栈里面,辅助栈的栈顶元素也需要弹出
        if(s_data.pop().equals(s_min.peek()))
            s_min.pop();
    }
    public int top() {
        return s_data.peek();
    }
    
    public int min() {
        return s_min.peek();
    }
}