JZ64 滑动窗口最大值

剑指offerJZ64 滑动窗口最大值

  • 目录
    • 题目描述
    • 题目理解
    • 方法一:暴力循环
    • 方法二:双端队列

目录

本文是参考牛客网的官方题解结合自己理解写的,官方题解:牛客官方题解
其中官方题解采用C++,博主已入JAVA坑,理解官方题解意思,使用JAVA写的

题目描述

给定一个数组和滑动窗口的大小,找出所有滑动窗口里数值的最大值。例如,如果输入数组{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]}。
窗口大于数组长度的时候,返回空

题目理解

给定一个数组num和一个窗口大小size,求每个窗口的最大值

方法一:暴力循环

这个题很容易想到一个暴力循环,外层循环我们遍历数组,内层循环遍历窗口,找到窗口中最大值记录下来到一个数组中即可,说干就干,这个代码博主是没看题解自己做的,对照牛客题解之后确实这样,博主的还节省了遍历循环次数.

package JZ64_Sliding_Window;
import java.util.*;
public class Max_Sliding_Window {
     
	public static void main(String[] args) {
     
	 Scanner sc=new Scanner(System.in);
 	  System.out.print("Enter a Matrix size:"); 
     int length=sc.nextInt();
//     System.out.print("Enter a Matrix:");
//     int[] matrix=new int[size];
//     for(int i=0;i
//   	  matrix[i]=sc.nextInt();      
//     }
     int[] matrix={
     2,3,4,2,6,2,5,1};
     System.out.print("Enter a Window size:");
     int size=sc.nextInt();
     ArrayList<Integer>  record=new ArrayList<>();
     if(size>length||size<=0||length<=0){
     
         return ;
     }
     int len=length-(size-1);
     int max=0;
     for(int i=0;i<len;i++) {
     
 	    max=matrix[i];
 	    for(int j=i+1;j<i+size;j++) {
     
 		  max=Math.max(max, matrix[j]);
 	    }
 	    record.add(max);
   }
     for(int i=0;i<len;i++) {
     
   	  System.out.print(record.toArray()[i]+"  ");
     }

	}
}

时间复杂度:O(n*k), 其中n为数组大小,k为窗口大小
空间复杂度:O(1),存结果必须要开的数组不算入额外空间

方法二:双端队列

双端队列这个没看牛客题解是没想到的,当时在暴力循环时候博主感觉到有一些循环浪费,每次滑窗就移动1个,那么前面的比较在我后面是不是可以用呢,我开始想着开一个数组记录,但是效果不太理想,看了题解才明白.
1.滑窗向右移动一步,如 {2,[3,4,2],6,2,5,1},新元素6如果和之前记录的数组里面的元素2,3,4进行比较,这个数组我们按照单调递增排列,只要6它大于数组中元素2,3,4,我们就可以删掉它们,一直比较到数组最后一个元素,一旦中间有元素大于新元素,就终止比较,然后把新元素插入队尾,就能保证队尾一直是最小的,在举得例子中此时数组比较完后只有6了.大家可以试着 {2,3,[4,2,6],2,5,1}滑窗比较下
2. 插入新比较元素,注意这里我们记录的是它在数组的下标,因为在后面我们要剔除不在滑窗中元素,那么根据什么剔呢?只能根据它的位置.
3. 检查队头最大元素是否仍在滑窗内,这里就利用到下标判断
4. 将数组最右边元素插入到记录数组中.

大家想想上面的数组我们其实一直是对数组最后元素或者数组开始的元素操作,即使中间比较过程,我们是一直把数组开始的元素删除,再重新比较数组开始元素,这样特性的数组是不是就双端队列呢?

package JZ64_Sliding_Window;
import java.util.*;

public class Deque_Sliding_window {
     
	
		public static void main(String[] args) {
     
		Scanner sc=new Scanner(System.in);
		System.out.print("Enter a Matrix size:"); 
	    int length=sc.nextInt();
	//   System.out.print("Enter a Matrix:");
	//   int[] matrix=new int[size];
	//   for(int i=0;i
//	 	  matrix[i]=sc.nextInt();      
	//   }
	    int[] matrix={
     2,3,4,2,6,2,5,1};
	    System.out.print("Enter a Window size:");
	    int size=sc.nextInt();
	    ArrayList<Integer>  record=new ArrayList<>();
	    Deque<Integer> deque=new LinkedList<Integer>();
	    for(int i=0;i<length;i++) {
     
	    	//只要队列不为空或者新比较元素都比队列中元素大就一直执行去队尾操作
	    	while(!deque.isEmpty()&&matrix[deque.getLast()]<matrix[i]) {
     
	    		deque.removeLast();
	    	}
	    	//能出while循环,要么是新的元素大于双端队列中所有值,已经删除队列中所有值
	    	//或者新元素已经小于队列中元素值
	    	//将新元素加入到队尾,保留下来与下一个新元素比较
	    	//注意队列记录的是数组中元素的下标,因为我们要检查队列中元素是否还在滑窗中
	    	deque.addLast(i);
	    	//检查队头下标,判断队头元素是否还在滑窗内
	    	if(deque.getFirst()+size<=i) {
     
	    		deque.removeFirst();
	    	}
	    	//判断是否构成了滑窗,如果构成,就记录队列中队头元素
	    	if(i>=size-1) {
     
	    		record.add(matrix[deque.getFirst()]);
	    	}
	    }
	    for(int i=0;i<record.size();i++) {
     
	     	  System.out.print(record.toArray()[i]+"  ");
	       }

	  }
	
}

你可能感兴趣的:(剑指offer,数据结构,队列,java,算法)