給定一個數組 nums,有一個大小為 k 的滑動窗口從數組的最左側移動到數組的最右側。你只可以看到在滑動窗口內的 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
提示:
1 <= nums.length <= 10^5
-10^4 <= nums[i] <= 10^4
1 <= k <= nums.length
暴力求解:超時
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {int maxnum=-10001;vector<int> result;int n= nums.size();//1、如果k> = nums.size, if(k>=n){for(int i=0;i<n;i++){if(nums[i]>=maxnum) maxnum=nums[i];}result.push_back(maxnum);return result;}//2、如果k<nums.sizeelse{int left=0;int right=0;for(left = 0;left<=n-k;left++){//更新左右邊界right=left+k-1;//更新最小值maxnum=-10001;for(int x=left;x<=right;x++){if(nums[x]>=maxnum) maxnum=nums[x]; }result.push_back(maxnum);}return result;}}
};
優化思路:
可以想到暴力方法肯定是有時間浪費的,在滑窗移動的時候,滑窗內插入一個新值,消失了一個舊值,有k-1個值仍然保留著。
我們只需要比較一下新插入的值和舊的k-1個值中的最大值,將得到的最大值賦給結果數組就可以了。
一開始我想的是用兩個容量為2的數組,分別存放第一大的數值和它的下標,第二大的數值和它的下標,但是在推導的時候發現一個問題:
錯誤代碼,具體疑問見代碼注釋:
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {int maxnum[2]={-10001,-10001}; //存儲當前滑窗內第一大和第二大的數值int index[2]={0,0}; //存儲當前滑窗內第一大和第二大的數值的下標vector<int> result;int n= nums.size();if(k*n==0) return result;//1、如果k> = nums.size, if(k>=n){for(int i=0;i<n;i++){if(nums[i]>=maxnum[0]) maxnum[0]=nums[i];}result.push_back(maxnum[0]);return result;}//2、如果k<nums.sizeelse{int left=0;int right=0;//將前k個數進行裝載for(int i=0;i<k;i++){//nums[i]比第一大還大,更新第一大if(nums[i]>=maxnum[0]){maxnum[0]=nums[i]; //第一大裝載完畢index[0]=i; } //第二大的范圍:大于等于舊的第二大,小于等于新的第一大,并且不能是第一大的數if(nums[i]>=maxnum[1] && nums[i]<=maxnum[0] && index[0]!=i){maxnum[1]=nums[i]; //第二大裝載完畢index[1]=i; }}left=1;//從第k+1個數開始for(right = k;right<n;right++,left++) {//如果移動的時候將最大值拋棄if(index[0]<left){//比較第二大的值和新插入的值的大小,獲取最大值//如果第二大值比新插入值大,則第二大值變為最大值,但是新插入的值不一定是第二大值//所以我們需要構建一個從大到小排列的雙端隊列!if(maxnum[1]>=)}result.push_back(maxnum);}return result;}}
};
發現需要構建一個從大到小排列的雙端隊列;
1、新的數入隊列,并按照其大小排列,然后將比它小的數全部出隊列(因為我們需要的是最大值,只要隊列中有數比新入隊列的數要小就說明它們絕對不可能是最大值,最大值最起碼也是大于等于新入隊列的數)。
2、觀察隊首(數值最大)的索引值是否在[left,right]之間,如果不在就出隊列,直到隊首索引值滿足在[left,right]之間
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {vector<int> result;int n= nums.size();if(n==0 ||k==0) return result;if(k>=n){auto maxPosition = max_element(nums.begin(), nums.end());result.push_back(*maxPosition);return result;}//2、如果k<nums.sizeelse{int left=0;int right=0;// 雙向隊列 保存當前窗口最大值的數組位置 保證隊列中數組位置的數值按從大到小排序deque< int> Dq;// 遍歷nums數組for(int i = 0;i < n;i++){// 保證從大到小 如果前面數小則需要依次彈出,直至滿足要求//如果隊尾的元素小于Nums[i],隊尾元素出隊列while(Dq.size() && nums[Dq.back()] <= nums[i]){Dq.pop_back();}// 將當前數組下標入隊列Dq.push_back(i);if(i>=k-1){// 判斷當前隊列中隊首是否有效,如果隊首小于左邊界,則將隊首出隊列,直到隊首元素符合要求while(Dq.size() && Dq.front() <= i-k){Dq.pop_front(); }result.push_back(nums[Dq.front()]);}}return result;}}
};
雖然AC了,但是效率不高,不過基本思路是符合大眾的:
不過上述的寫法仍然有些不簡潔,這里貼一下比較簡潔的寫法:
https://leetcode-cn.com/problems/sliding-window-maximum/solution/dan-diao-dui-lie-by-labuladong/
官方給出的dp思路,還得理解理解
https://leetcode-cn.com/problems/sliding-window-maximum/submissions/
class Solution {
public:vector<int> maxSlidingWindow(vector<int>& nums, int k) {int n = nums.size();vector<int> output(n-k+1,0);vector<int> left(n,0);vector<int> right(n,0);left[0]=nums[0];right[n-1]=nums[n-1];for(int i = 1;i<n;i++){if(i%k == 0) left[i]=nums[i];else left[i]=max(left[i-1],nums[i]);int j = n-i-1;if((j+1)%k == 0) right[j]=nums[j];else right[j]=max(right[j+1],nums[j]);}for(int i = 0;i < n-k+1;i++)output[i] = max(left[i+k-1],right[i]);return output;}
};
為什么:兩數組一起可以提供兩個塊內元素的全部信息。
考慮從下標 i 到下標 j的滑動窗口。 根據定義,right[i] 是左側塊內的最大元素, left[j] 是右側塊內的最大元素。因此滑動窗口中的最大元素為 max(right[i], left[j])