单调队列和Min栈总结

132 阅读1分钟

单调队列总结

单调队列是指队列中的元素是按照单调递增或者递减的顺序排列的,一般常用于需要动态求某个范围的最值的情况

单调队列一般使用双向队列来实现,具体实现细节见下面:

// 创建单调队列
Deque<Integer> monoQueue = new LinkedList<>();

// 增加元素,从队列尾部增加元素,如果有比当前元素小的则弹出,维持单调队列
// 因为单调队列是为了维护当前范围内的最大值,那么比当前元素小的元素必然不可能是最大值,因此这些元素删除不会有影响
public void push_back(int value) {
  	while (!monoQueue.isEmpty() && monoQueue.peek() < value) {
  			monoQueue.pollLast();
		}
  	monoQueue.offer(value);
}

// 删除元素:需要满足条件,比如单调队列头元素等于...,则删除

队列的最大值

剑指Offer 59- II 队列的最大值,为了实时维护当前队列中的最大值,可以使用单调递减队列,队头元素表示当前队列中的最大值,因此增加元素时和之前一样,而删除元素时当单调队列的队头元素等于队列的队头元素时,删除,否则不必。因为队列的队头元素如果是队列中的最大元素,那么必然单调队列的队头元素就等于队列的队头元素,因此删除;而如果它不是最大元素,那么必然队列后面的元素是最大元素,则单调队列的队头元素就是队列后面的元素,在单调队列中队列头元素早已经被删去了,不用再维护。

class MaxQueue {
    private Queue<Integer> queue;
    private Deque<Integer> maxQueue;
    
    public MaxQueue() {
        queue = new LinkedList<>();
        maxQueue = new LinkedList<>();
    }
    
    public int max_value() {
        if (queue.isEmpty())
            return -1;
        return maxQueue.peek();
    }
    
    public void push_back(int value) {
        queue.offer(value);
        while (!maxQueue.isEmpty() && maxQueue.peekLast() < value) {
            maxQueue.pollLast();
        }
        maxQueue.offer(value);
    }
    
    public int pop_front() {
        if (queue.isEmpty())
            return -1;
        int res = queue.poll();
        if (maxQueue.peek() == res) {
            maxQueue.poll();
        }
        return res;
    }
}

/**
 * Your MaxQueue object will be instantiated and called as such:
 * MaxQueue obj = new MaxQueue();
 * int param_1 = obj.max_value();
 * obj.push_back(value);
 * int param_3 = obj.pop_front();
 */

从这道题我们可以看出,单调队列其实对应了队列的元素的状态,但不是一一对应的。

还有类似的题可以使用单调队列,即 剑指Offer 59-I 滑动窗口的最大值

单调栈

既然有单调队列,那么就有单调栈。可以看这道题 剑指Offer 30.包含min函数的栈

单调栈是指单调栈中每一个元素都是对应栈中的最小值,单调栈中的每一个元素都和栈中元素一一对应。所以,压栈的时候,需要压入一个栈元素和单调栈顶元素的最小值;退栈的时候,同步推出即可,求最小值只需返回单调栈栈顶元素,如果单调栈不为空

class MinStack {
    private Deque<Integer> stack;
    private Deque<Integer> minStack;

    /** initialize your data structure here. */
    public MinStack() {
        stack = new LinkedList<>();
        minStack = new LinkedList<>();
    }
    
    public void push(int x) {
        stack.push(x);
        int min;
        if (minStack.isEmpty()) {
            min = x;
        } else {
            min = Math.min(minStack.peek(), x);
        }
        minStack.push(min);
    }
    
    public void pop() {
        if (stack.isEmpty()) {
            return;
        }
        stack.pop();
        minStack.pop();
    }
    
    public int top() {
        if (stack.isEmpty())
            return -1;
        return stack.peek();
    }
    
    public int min() {
        if (minStack.isEmpty())
            return -1;
        return minStack.peek();
    }
}

/**
 * Your MinStack object will be instantiated and called as such:
 * MinStack obj = new MinStack();
 * obj.push(x);
 * obj.pop();
 * int param_3 = obj.top();
 * int param_4 = obj.min();
 */