0
点赞
收藏
分享

微信扫一扫

《剑指offer》-day 27 栈与队列(困难)【单调队列】

天涯学馆 2022-04-30 阅读 35

剑指 Offer 59 - I. 滑动窗口的最大值

在这里插入图片描述在这里插入图片描述

单调队列

思路 🤔

  • 维护一个队列,其单调性为“从队头到队尾单调减”,可对其操作如下:
  • add(int val):若当前元素 v a l val val > 队尾元素,则需要维护单调性,即队尾出队,直至满足单调性为止(从队头到队尾单调减);否则,则将当前元素 v a l val val 直接插入队尾,即可
  • remove(int val):若当前元素 v a l val val == 队头元素,则将队头元素出队;否则,不做任何操作。
class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        if (nums == null || nums.length < k || k == 0) 
            return new int[] {};
        int n = nums.length;
        int[] res = new int[n - k + 1];
        // 先处理前 k - 1 个
        MyQueue queue = new MyQueue();
        int i = 0;
        int index = 0;
        for ( ; i < k - 1; i++) {
            queue.add(nums[i]);
        }
        for ( ; i < n; i++) {
            queue.add(nums[i]);
            res[index++] = queue.peek();
            queue.remove(nums[i - k + 1]);
        }
        return res;
    }
}

class MyQueue {
   Deque<Integer> queue = new LinkedList<>();

    public void add(int val) {
        // 维护单调性(从队头到队尾单调减)
        while (!queue.isEmpty() && val > queue.peekLast()) { // 和队尾比较
            queue.pollLast();
        }
        queue.offer(val);
    }

    public int peek() {
        if (queue.isEmpty()) {
            throw new NullPointerException();
        }
        return queue.peek();
    }

    public void remove(int val) {
        if (queue.isEmpty()) {
            throw new NullPointerException();
        }
        // 窗口左端元素和对头元素相同时,则队头出队
        if (val == this.peek()) {
            queue.poll();
        }
    }
}
  • 使用双端队列,而不是单端队列;
  • 维护单调性时(从队头到队尾单调减),是将当前元素 v a l val val队尾元素比较(而不是队头元素)

在这里插入图片描述

剑指 Offer 59 - II. 队列的最大值

在这里插入图片描述在这里插入图片描述

双队列:单调队列 + 普通队列

一种朴素的解法是,使用一个普通队列,push_backpop_front 的时间复杂度均为 O ( 1 ) O(1) O(1)

但是,此时 max_value 需要遍历队列中所有元素,时间复杂度为 O ( n ) O(n) O(n)

更优的解法是:使用一个普通队列,同时额外再维护一个 单调队列,具体步骤如下。

思路 🤔

  • 额外维护一个单调队列 descendQueue ,其单调性为“从队头到队尾单调减”
  • max_value():单调队列 descendQueue 队头元素,即为当前最大元素;
  • push_back(int value):入队
    • 首先,需要维护 descendQueue 的单调性(队头到队尾单调递减)
    • 然后,将 v a l u e value value 同时入队 queuedescendQueue
  • pop_front():出队
    • 队列 queue 为空时,直接返回 − 1 -1 1,即可;
    • descendQueue 队头元素和 queue 队头元素相等,则二者队头元素同时出队;
    • 否则,即两个队头不相等,则仅仅 queue 队头出队。
class MaxQueue {
    Deque<Integer> queue = new LinkedList<>();
    Deque<Integer> descendQueue = new LinkedList<>();

    public MaxQueue() {
    }
    
    public int max_value() {
        if (descendQueue.isEmpty()) {
            return -1;
        }
        return descendQueue.peekFirst();
    }
    
    public void push_back(int value) {
        // 维护descendQueue的单调性(对头到对尾单调递减)
        while (!descendQueue.isEmpty() 
            && value > descendQueue.peekLast()) {
            descendQueue.pollLast();
        }
        // 同时入队
        queue.offerLast(value);
        descendQueue.offerLast(value);
    }
    
    public int pop_front() {
        if (queue.isEmpty()) {
            return -1;
        }
        if (!descendQueue.isEmpty() 
            && descendQueue.peek().equals(queue.peek())) {
            queue.pollFirst();
            return descendQueue.pollFirst();
        }
        return queue.pollFirst();
    }
}

/**
 * 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();
 */

在这里插入图片描述

举报

相关推荐

0 条评论