题目描述:
给定一个数组 nums 和滑动窗口的大小 k,请找出所有滑动窗口里的最大值。
示例:
输入: nums = [1,3,-1,-3,5,3,6,7], 和 k = 3
输出: [3,3,5,5,6,7]
解释:
最后一列是滑动窗口的最大值
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
提示:
你可以假设 k 总是有效的,在输入数组不为空的情况下,1 ≤ k ≤ 输入数组的大小
解题思路:
这个题是一个滑动窗口相对简单一点的题目。利用一个双向链表存放窗口最大值。
这个双向链表从头到尾,从大到小排序。当我们向链表中添加新元素时,要判断这个元素是否比链表的尾部大,如果大于尾部元素,就一次弹出尾部元素,直到尾部元素比新元素大或者链表为空停止。然后加入这个元素,接下来当窗口滑动时,判断过期的元素把它弹出即可。
具体代码如下:
import java.util.Arrays;
import java.util.LinkedList;
public class 滑动窗口的最大值 {
public static int[] maxSlidingWindow(int[] nums, int w) {
int[] n = {};
if (nums == null || w < 1 || nums.length < w) {
return n;
}
int len = nums.length;
int index = 0;
LinkedList<Integer> linkedList = new LinkedList<Integer>();
int[] res = new int[len - w + 1];
for (int i = 0; i < len; i++) {
// 进链表前看是否需要弹出
while (!linkedList.isEmpty() && nums[linkedList.peekLast()] <= nums[i]) {
linkedList.pollLast();
}
linkedList.add(i);
// 弹出过期的元素
if (linkedList.peekFirst() == i - w) {
linkedList.pollFirst();
}
// 窗口满3必有一个最大值
if (i >= w - 1) {
res[index++] = nums[linkedList.peekFirst()];
}
}
return res;
}
//测试
public static void main(String[] args) {
int[] nums = {1,3,-1,-3,5,3,6,7};
int k = 3 ;
int[] res1 = maxSlidingWindow(nums,k);
System.out.println(Arrays.toString(res1));
}
}
快一年了,回头再看这一道题,发现自己是真的菜。当初是跟着左神学的,听完左神的讲解就随机练了代码。今天再看这道题发现还是不会。附上今天学习的两种方法。
public static int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
// 1. 优先队列存放的是二元组(num,index) : 大顶堆(元素大小不同按元素大小排列,元素大小相同按下标进行排列)
// num : 是为了比较元素大小
// index : 是为了判断窗口的大小是否超出范围
PriorityQueue<int[]> pq = new PriorityQueue<int[]>(new Comparator<int[]>(){
public int compare(int[] pair1,int[] pair2){
return pair1[0] != pair2[0] ? pair2[0] - pair1[0]:pair2[1] - pair1[1];
}
});
// 2. 优选队列初始化 : k个元素的堆
for(int i = 0;i < k;i++){
pq.offer(new int[]{nums[i],i});
}
// 3. 处理堆逻辑
int[] res = new int[n - k + 1]; // 初始化结果数组长度 :一共有 n - k + 1个窗口
res[0] = pq.peek()[0]; // 初始化res[0] : 拿出目前堆顶的元素
for(int i = k;i < n;i++){ // 向右移动滑动窗口
pq.offer(new int[]{nums[i],i}); // 加入大顶堆中
while(pq.peek()[1] <= i - k){ // 判断潜在最大值是否在区间内不在则删除。所以优先队列中 index可以不排序,直接为return pair2[0] - pair1[0];
pq.poll(); // 维护:堆 的大小就是滑动窗口的大小
}
res[i - k + 1] = pq.peek()[0]; // 此时堆顶元素就是滑动窗口的最大值
}
return res;
}
方法二:单调栈
public int[] maxSlidingWindow(int[] nums, int k) {
int n = nums.length;
Deque<Integer> dq = new LinkedList<>();
for(int i = 0;i<k;i++)
{
while (!dq.isEmpty()&&nums[i]>nums[dq.peekLast()])
{
dq.pollLast();
}
dq.offerLast(i);
}
int[] ans = new int[n+1-k];
ans[0] = nums[dq.peekFirst()];
for(int i = k;i<n;i++)
{
while (!dq.isEmpty()&&nums[i]>nums[dq.peekLast()])
{
dq.pollLast();
}
dq.offerLast(i);
if(dq.peekFirst()<=i-k)
{
dq.pollFirst();
}
ans[i-k+1] = nums[dq.peekFirst()];
}
return ans;
}