C代码:单调队列
int* maxSlidingWindow(int* nums, int numsSize, int k, int* returnSize){
int q[numsSize];
int l = 0, r = 0;
for (int i = 0; i < k; ++i) {
while (l < r && nums[i] >= nums[q[r-1]]) { // >= 很重要
--r;
}
q[r++] = i; // 数组构造单调队列,头最大;
} // 队列q[]存放下标i, why? 因为队列头虽然最大,但是有不在滑动窗口的数据,
// 如何区分数据在不在滑动窗口中,存放下标!
int* ans = malloc(sizeof(int) * (numsSize - k +1));
int j = 0;
ans[j++] = nums[q[l]];
for (int i = k; i < numsSize; ++i) {
while (l < r && nums[i] >= nums[q[r-1]]) {
--r;
} // >= 很重要, 滑动窗口为一段的最大值,若不放入进去,就会缺少值
q[r++] = i;
while (q[l] <= i - k) { // q[]数组里面的值并没有减少,操作指针实现!
++l;
}
ans[j++] = nums[q[l]]; // 记录最大值之前要确保 队列最大值在滑动窗口之内
}
*returnSize = j;
return ans;
}
Java代码1:优先级队列、O(nlogn)
初始时,我们将数组 nums 的前 k 个元素放入优先队列中。
每当我们向右移动窗口时,我们就可以把一个新的元素放入优先队列中,此时堆顶的元素就是堆中所有元素的最大值。
然而这个最大值可能并不在滑动窗口中,在这种情况下,这个值在数组 nums 中的位置出现在滑动窗口左边界的左侧。
因此,当我们后续继续向右移动窗口时,这个值就永远不可能出现在滑动窗口中了,我们可以将其永久地从优先队列中移除。
这个方法在宏观上 更容易理解,相当于封装了;
代码2,就在处理排序的细节了,相当于在代码1的基础上优化,思路还是不错的。
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
// PriorityQueue<> pq = new Priority<>(); 就是一个容器;
// int[]是容器存放的内容,始终可以根据条件对数组排序。
PriorityQueue<int[]> pq = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] pair1, int[] pair2) {
// 不相等 ? 按值降序 :按下标降序
return pair1[0] != pair2[0] ? pair2[0] - pair1[0] : pair2[1] - pair1[1];
}
});
// offer poll peek
for (int i = 0; i < k; ++i) {
pq.offer(new int[]{nums[i], i});
}
int[] ans = new int[n - k + 1];
ans[0] = pq.peek()[0];
for (int i = k; i < n; ++i) {
pq.offer(new int[]{nums[i], i});
// 移除不在滑动窗口的最大值,此处 k 为滑动窗口长度
while (pq.peek()[1] <= i - k) {
pq.poll();
}
ans[i - k + 1] = pq.peek()[0];
}
return ans;
}
Java代码2:单调队列、O(n)
在优先级队列的思路上进行优化。
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> deque = new LinkedList<>();
for (int i = 0; i < k; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast(); // 队列开头永远是最大值:5 -3、5 3 7(不存在,while会把5 3干掉)
}
deque.offerLast(i); // 放入下标
}
int[] ans = new int[n - k + 1];
ans[0] = nums[deque.peekFirst()]; // 上面确保deque的第一值最大
for (int i = k; i < n; ++i) {
while (!deque.isEmpty() && nums[i] >= nums[deque.peekLast()]) {
deque.pollLast();
}
deque.offerLast(i);
while (deque.peekFirst() <= i - k) { //上面 nums[i] 大于 队列末尾值,才替代;要是小于的话就不;
deque.pollFirst();
}
ans[i - k + 1] = nums[deque.peekFirst()];
}
return ans;
}
Java代码3:暴力,超时,O(n * k)
暴力的解题思路比较直观!可借鉴。
但是,暴力在遍历过程中有重复的部分。
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length -k + 1;
int[] arr = new int[n];
for (int i = 0; i < n; ++i) {
int num = nums[i];
for (int j = i; j < i + k; ++j) {
num = Math.max(num, nums[j]);
}
arr[i] = num;
}
return arr;
}