输入:一个数组nums,一个窗口大小k。
输出:每个窗口范围内的最大值。
规则:从数组最左端移动到最右端,每次只能看到窗口范围内的元素,找到最大值。之后向右移动一位。
分析:重点是维护一个数据结构,结构内只有窗口内的元素,而且还要比较能方便的插入、删除。要求时间复杂度是O(n)。
分析1:暴力求解,就不写了,非常简单。
public int[] maxSlidingWindow(int[] nums, int k) {
int n =nums.length;
if(n==0) return new int[]{};
int[] result = new int[n-k+1];
int idx = 0;
for(int i=0;i<=n-k;i++){
int max = Integer.MIN_VALUE;
for(int j =i ;j
学习1:因为要最大值,所以首先想到使用优先队列。维持一个大小为k的最大堆。堆顶元素就是最大值。每次插入元素,获取最大值之后,就把窗口最左端元素删除,保证堆内元素是在窗口范围内。
暂时不考虑堆的删除操作,时间复杂度也是O(Nlogk)。
public int[] maxSlidingWindowV2(int[] nums, int k) {
int n =nums.length;
if(n==0) return new int[]{};
int[] result = new int[n-k+1];
int idx = 0;
PriorityQueue maxHeap = new PriorityQueue(k,new Comparator(){
@Override
public int compare(Integer i1,Integer i2){
return i2-i1;
}
});
for(int i=0;i
学习2:使用双端队列,队列中的元素是数组的值。队列中元素从大到小排序。每次插入的时候,从尾部插入,插入的时候将比自己小的元素删除,最后插入自己。这样保证队列头部的元素是最大值。如何保证队列中的元素是窗口范围内的元素:每次插入一个元素之后,将窗口最左端的元素删除。删除的时候有个技巧是,如果在队列头部就删除,不在队列头部可以暂时保留,我们只要保证队列头部的元素在窗口范围内即可。
这里要学习的首先是维护队列有序。在队列尾部插入,插入的时候将比当前元素小的值删除。删除的时候只删除头部元素。我想这里就是烧脑的地方。想了很久没想明白怎么这么神奇,只要删除头部元素就行。
这个时间复杂度是O(n),因为每个元素入队一次,出队一次。
例如i=4,k=3,则本次窗口范围内元素是nums[2,],nums[3],nums[4]。获取最大值之后,需要删除nums[2]。
如果队列头部是nums[2],也就是说nums[2]是最大值,那直接删除,nums[3],nums[4]是属于下一个窗口的元素,没有问题。
如果队列头部不是nums[2],那nums[2]一定不在队列中,因为我们要维护队列从大到小排序,在插入的时候,会把比自己小的元素从队列删除。如果最大值是nums[3],那么在插入的时候nums[2]就被删除了。队列中只有nums[3]和nums[4]。他们是属于下一个窗口的元素,没有问题。
如果最大值是nums[4],那么在插入nums[4]的时候,会把nums[2]和nums[3]都从队列移除,队列中只有nums[4],是属于下一个窗口的元素,没有问题。
所以只需要判断队列头部是不是nums[2]即可。
public int[] maxSlidingWindowV3(int[] nums, int k) {
int n =nums.length;
if(n==0) return new int[]{};
int[] result = new int[n-k+1];
int idx = 0;
Monoque queue = new Monoque();
for(int i=0;i queue = new LinkedList<>();
public void push(Integer n){
while(!queue.isEmpty() && queue.peekLast()