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

it2023-08-18  66

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; } }
最新回复(0)