题目:给定一个数组nums,有一个滑动大小为k的窗口从左往右滑动。每一次走一步,
求窗口中最大的值。
分享三种思路:
1.是暴力解法
每次滑动窗口,遍历窗口中k个数的最大值。
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
if (k == 1) {
return nums;
}
int[] res = new int[nums.length - k + 1];
for (int i = 0; i < nums.length - k + 1; i++) {
int max = Integer.MIN_VALUE;
for (int j = i; j < i + k; j++) {
max = Math.max(nums[j], max);
}
res[i] = max;
}
return res;
}
时间复杂度为 O(n*k)
2. 用双向队列实现
思想用双向队列保存窗口中的数的索引。 如果新滑入窗口的数比窗口左边的数大,就从队列中清除掉索引。
如图所示,窗口中数字为4,1,2,3。索引分别为2,3,4,5
在3新滑入到窗口时, 3的左边的1,2比3小,一定不会出现在窗口最大值中,所有做的操作是在双向队列中删除这些索引。如图所示的双向队列。这样双向队列每次最左边的数字永远是保存的是最大数的数组索引坐标。
public int[] maxSlidingWindow(int[] nums, int k) {
if (nums == null || nums.length == 0) {
return new int[0];
}
if (k == 1) {
return nums;
}
int[] res = new int[nums.length - k + 1];
ArrayDeque<Integer> deq = new ArrayDeque<Integer>();
int index = 0;
for (int i = 0; i < nums.length; i++) {
if (i >= k && deq.getFirst() <= i - k) { // 如果双向列表索引,超过边界就清理
deq.removeFirst();
}
while (deq.size() > 0 && nums[deq.getLast()] <= nums[i]) { //删除比最右边数小的索引
deq.removeLast();
}
deq.add(i); //加入到双向队列
if (i >= k - 1) {
res[index++] = nums[deq.getFirst()]; //双向队列头,永远是保存的窗口最大值的索引
}
}
return res;
}
时间复杂度为O(n)
3. 动态规划
算法的思想很巧妙,就是难的想到。这个方法是参考力扣上官方解法讲解的。写下来记录下来思想和代码。参考
算法的思想是把数组分成n块。
开头元素为 i ,结尾元素为 j 的当前滑动窗口可能在一个块内,也可能在两个块中。
建立数组 left, 其中 left[j] 是从块的开始index到下标 index+k -1最大的元素,方向 左->右。
建立反向数组right,是从块的结束到下标index最大元素,方向 右 ->左。
情况2,可以有两部分组成,一个是左块的一部分,加上右块的一部分。如下所示:
左边的一部分用反向数组right记录最大值,右边的一部分用left数组记录最大值。
最后求两个数组的最大值,即为窗口最大值。
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++) {
// left
if (i % k == 0) {
left[i] = nums[i];
} else {
left[i] = Math.max(left[i - 1], nums[i]);
}
// right
if ((n - i) % k == 0) {
right[n - i - 1] = nums[n - i - 1];
} else {
right[n - i - 1] = Math.max(nums[n - i - 1], right[n - i]);
}
}
int[] res= new int[n - k + 1];
for(int i = k - 1 ; i < n ; i++) {
res[i - k + 1] = Math.max(left[i], right[i - k + 1]);
}
return res;
}