1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| class Solution { public int[] maxSlidingWindow(int[] nums, int k) { LinkedList<Integer> queue = new LinkedList<>(); int[] ans = new int[nums.length - k + 1]; for (int i = 0; i < nums.length; i++) { if (!queue.isEmpty() && queue.peek() < i - k + 1) { queue.poll(); } while (!queue.isEmpty() && nums[queue.peekLast()] <= nums[i]) { queue.pollLast(); } queue.offer(i); if (i - k + 1 >= 0) { ans[i - k + 1] = nums[queue.peek()]; } } return ans; } }
|
学习了一个重要的数据结构: Monotonic Deque. 具体见tricks中对monotonic stack的讨论.
时间复杂度: O(n)
空间复杂度: O(k)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| class Solution { public int[] maxSlidingWindow(int[] nums, int k) { int[] leftMax = new int[nums.length]; int currLeftMax = nums[0]; leftMax[0] = currLeftMax; for (int i = 1; i < nums.length; i++) { currLeftMax = i % k == 0 ? nums[i] : Math.max(currLeftMax, nums[i]); leftMax[i] = currLeftMax; }
int[] rightMax = new int[nums.length]; int currRightMax = nums[nums.length - 1]; rightMax[rightMax.length - 1] = currRightMax; for (int i = nums.length - 2; i >= 0; i--) { currRightMax = (i + 1) % k == 0 ? nums[i] : Math.max(currRightMax, nums[i]); rightMax[i] = currRightMax; } int[] ans = new int[nums.length - k + 1]; for (int i = 0; i < ans.length; i++) { ans[i] = Math.max(rightMax[i], leftMax[k + i - 1]); } return ans; } }
|
这个做法我是真想不出来. 不知道想出这个答案的思路是什么.
原理很容易理解. 把nums划分成block, 每个block的长度为k, 除了最后一个block的长度可能小于k, 这是没问题的. leftMax装的东西这样理解. 在某个位置i, 它一定属于某个block, 那么leftMax[i]表示的是从它所在的block的开头到这里最大的值. rightMax装的东西则是, 在某个位置j, 它一定属于某个block, 那么从该rightMax[i]表示的是从它所在的block的结尾到这里的最大值. 于是我们的sliding window在某个位置时, 要么window刚好fit进一个block, 此时最大值就是rightMax[blockStart]或者leftMax[blockEnd]; 要么window横跨两个block, 此时最大值就是Math.max(rightMax[blockStart], leftMax[blockEnd]).
这个方法是真的巧. 我不懂的是为何想到要划分block这件事, 以及block的size为何确定成k, 既不大于k, 也不小于k.
时间复杂度: O(n)
空间复杂度: O(n)