leetcode. [239]滑动窗口最大值(B-F(暴力实现),PQ(优先队列),Deque(双端队列),DP(动态规划))

  1. Sliding Window Maximum
    在这里插入图片描述
    在这里插入图片描述

分析

我们将数组从0扫描到n-1,将“promising”元素保留在双端队列中。 每个元素放置一次并轮询一次时,该算法将摊销O(n)。

在每个i处,我们保留“promising”元素,它们可能是窗口[i-(k-1),i]或任何后续窗口中的最大数量。 这意味着

如果双端队列中的某个元素不在i-(k-1)范围内,我们将其丢弃。 我们只需要从头开始轮询,因为我们使用的是双端队列,并且元素按数组中的序列排序

现在,只有[i-(k-1),i]中的那些元素在双端队列中。 然后,我们从尾部丢弃小于a [i]的元素。 这是因为如果a [x] <a [i]和x <i,则a [x]没有机会成为[i-(k-1),i]或任何其他后续窗口中的“最大值” :a [i]将永远是一个更好的候选人。

结果,双端队列中的元素按数组中的顺序及其值进行排序。 在每一步,双端队列的头部都是[i-(k-1),i]中的max元素

代码

其中:
public E peek():检索但不删除此列表的头部(第一个元素)。

public E peekFirst():检索但不删除此列表的第一个元素,如果此列表为空,则返回null。

public E peekLast():检索但不删除此列表的最后一个元素,如果此列表为空,则返回null。

方法一:

  • 双端队列实现

class Solution {
       if (nums == null || nums.length < 2)
           return nums;
       //双向队列,保存当前窗口最大值的数组位置 保证队列中数组位置的数值按从大到小排序
        LinkedList<Integer> queue = new LinkedList<>();
        //结果数组
        int[] result = new int[nums.length - k + 1];
        //遍历nums数组
        for (int i = 0; i < nums.length; i++) {
            //保证从大到小 如果前面数小则需要依次弹出,直至满足要求
            while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]){
                queue.pollLast();
            }
            //添加当前值对应的数组下标
            queue.addLast(i);
            //判断当前队列中队首的值是否有效
            if (queue.peek() <= i - k){
                queue.poll();
            }
            //当窗口长度为k时,保存当前窗口中最大值
            if (i+1 >= k){
                result[i+1-k] = nums[queue.peek()];
            }
        }
        return result;
    }
}
//leetcode submit region end(Prohibit modification and deletion)

方法二:暴力

最简单直接的方法是遍历每个滑动窗口,找到每个窗口的最大值。一共有 N - k + 1 个滑动窗口,每个有 k 个元素,于是算法的时间复杂度为 {O}(N k),表现较差。

public int[] maxSlidingWindow(int[] nums, int k) {
    //暴力实现 B-F
        int n = nums.length;
        if (n * k == 0)
            return new int[0];
        int[] output = new int[n - k + 1];
        for (int i = 0; i < n - k + 1; i++) {
            int max = Integer.MIN_VALUE;
            for (int j = i; j < i + k; j++) {
                max = Math.max(max , nums[j]);
            }
            output[i] = max;
        }
        return output;

方法三:动态规划

class Solution {
  public int[] maxSlidingWindow(int[] nums, int k) {
    int n = nums.length;
    if (n * k == 0) return new int[0];
    if (k == 1) return nums;

    int [] left = new int[n];
    left[0] = nums[0];
    int [] right = new int[n];
    right[n - 1] = nums[n - 1];
    for (int i = 1; i < n; i++) {
      // from left to right
      if (i % k == 0) left[i] = nums[i];  // block_start
      else left[i] = Math.max(left[i - 1], nums[i]);

      // from right to left
      int j = n - i - 1;
      if ((j + 1) % k == 0) right[j] = nums[j];  // block_end
      else right[j] = Math.max(right[j + 1], nums[j]);
    }

    int [] output = new int[n - k + 1];
    for (int i = 0; i < n - k + 1; i++)
      output[i] = Math.max(left[i + k - 1], right[i]);

    return output;
  }
}

posted @ 2020-10-20 20:08  your_棒棒糖  阅读(54)  评论(0编辑  收藏  举报