标签:队列,堆,集合
题目描述
给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{2,3,4,2,6,2,5,1}及滑动窗口的大小3,那么一共存在6个滑动窗口,他们的最大值分别为{4,4,6,6,6,5}; 针对数组{2,3,4,2,6,2,5,1}的滑动窗口有以下6个: {[2,3,4],2,6,2,5,1}, {2,[3,4,2],6,2,5,1}, {2,3,[4,2,6],2,5,1}, {2,3,4,[2,6,2],5,1}, {2,3,4,2,[6,2,5],1}, {2,3,4,2,6,[2,5,1]}。
解题思路
- 法一:简单的暴力法
- 法二:双向队列
用一个双向队列,队列第一个位置保存当前窗口的最大值,当窗口滑动一次,判断当前最大值是否过期(当前最大值的位置是不是在窗口之外),新增加的值从队尾开始比较,把所有比他小的值丢掉。这样时间复杂度为O(n)。 - 法三:大顶堆(复杂度太高,不建议)
参考代码
双端对列法:不理解的可以看下把每次队列列的size输出的结果:1 1 1 2 1 2 2 2
课件大部分时候队列只有几个元素
//用双端对列保存的是num中的下标,
//从双端队列q的左边弹出元素,从右边压入元素(同时删除小的)
public ArrayList<Integer> maxInWindows(int[] num, int size){
ArrayList<Integer> res = new ArrayList<>();
if(num.length == 0 || size > num.length || size <= 0){
return res;
}
Deque<Integer> q = new LinkedList<>();
//q保存的是num中的下标,从双端队列q的左边弹出元素,从右边压入元素
//保证存在q中的下标所对应在num里的元素是单调递减的,
//这样窗口中的最大值一直都是最左边的元素,也就是first元素
for(int i=0; i<num.length; i++){
//从左边删除过期元素
//用来控制窗口的大小始终为size, i是当前下标(这一轮窗口的右边下标),
//q的first是窗口的左边下标,保证窗口左边 > 窗口右边 - 窗口大小;
//队列左边<= i-size 的部分是过期的窗口,需要从队列中 删除
while(!q.isEmpty() && q.peekFirst() <= i-size){
q.pollFirst();
}
//从队列的右边压入元素, 压入的元素如果大于当前窗口右边的元素,
//则可以将窗口右边较小的元素删除(此时需要保证这个窗口的元素从左到右是单调递减的)
while(!q.isEmpty() && num[q.peekLast()]<num[i]){
q.pollLast();
}
q.offerLast(i);
//当i >= size-1时,第一个窗口出现,开始输出结果
if(i >= size - 1)
res.add(num[q.peekFirst()]);
}
return res;
}
暴力法
import java.util.ArrayList;
public class Solution {
//暴力,两个循环,外循环记录窗口移动,内循环寻找当前窗口最大值
//时间O(n^2)
public ArrayList<Integer> maxInWindows(int [] num, int size)
{
ArrayList<Integer> res = new ArrayList<>();
if(num==null || num.length == 0 || num.length < size || size <= 0)
return res;
for(int i=0; i<num.length-size+1; i++){
int max = num[i];
for(int j=0 ;j<size; j++){
if(max < num[i+j])
max = num[i+j];
}
res.add(max);
}
return res;
}
}
大顶堆法
//用堆的思想,优先堆列实现,它底层是堆排序的实现,不用自己再建堆和排序调整了
//复杂度很高,O(n*n*logn)
public ArrayList<Integer> maxInWindows(int[] num, int size){
ArrayList<Integer> res = new ArrayList<>();
if(num.length == 0 || size > num.length || size <= 0){
return res;
}
//因为每次堆顶是保存最大元素,因此是大顶堆
PriorityQueue<Integer> maxHeap = new PriorityQueue<>(size,
(Integer a, Integer b ) -> b - a);
for(int i=0; i<size; i++){
maxHeap.offer(num[i]);
}
res.add(maxHeap.peek());
// 定义删除的下标,最早的数组先删
int flag = 0;
for(int j=size; j<num.length; j++){
//先加再删和先删再加
maxHeap.remove(num[flag++]);
maxHeap.offer(num[j]);
res.add(maxHeap.peek());
}
return res;
}