滑動窗口
1.概念
滑動窗口是一類很常見的題型,最常見的就是子串問題,因為滑動窗口是一個連續的,所以很容易就是問滿足條件的最大或者最小子串啊,這個條件就是不同的地方,但萬變不離其宗,滑動窗口就是一個窗口的移動。
總之:子串+最值 --> 滑動窗口
滑動窗口有兩大類
- 固定長度的滑動窗口:窗口的大小是固定好的,這是其實分為了窗口形成和人窗口滑動兩個過程,窗口形成就是要先讓窗口達到要求的長度; 窗口滑動的過程在右邊界長的時候,左邊界也要跟着長,維持窗口長度不變;
- 可變長度的滑動窗口:這也是遇到最多的,控制窗口移動的原因不是長度,而是是否達到題目中某一條件,如果達到此條件,那右邊界停下,左邊界開始移動,試圖去破壞這一條件,就是在這個過程中不斷的更新結果。
2.過程
- 1.從題目中先整理出條件,常見的比如說子序列的和大於某值,子串中包含某些值,子串中出現了重復值,先把這些條件找出來,這就是用來移動窗口的依據;
- 2.初始化left和right指針都為0,right指針從頭走到尾,當不滿足上面的條件時,right走,一直走到這個窗口滿足上面我們總結出來的條件了,停下;
- 3.記錄下我們要的答案,比如最典型的問子串的最小長度啊啥的,記錄下這時候我們的right-left+1,這就是目前窗口的大小;
- 4.右邊走不動了那左邊就得開始走了,移動左指針,每次移動都要把左指針的值給去除,因為我們統計的結果只能是窗口里的結果,出去了自然就不要了,然后,每一次左指針的移動都要判斷是否滿足條件,比如要求子串和大於某一值,左邊移動一步,減去這個值看還大於目標值嗎,如果滿足,更新我們最終要的最小子串,因為這時候子串長度肯定縮小了嘛,直到不滿足條件為止;如果不滿足,那左指針不用再動了,右指針可以開始動了,尋找下一個滿足條件時停下來。
- 5.就這樣重復,右邊動完左邊動,左邊動完右邊再動;
總結:右邊界要使窗口達到某一條件,左邊界使窗口跳出這一條件。
3.模板
int left = 0, right = 0;
while(right < nums.length){
result = result + nums[right];
//更新窗口內數據;
//將移入窗口的值添加進結果,這里就是不同題目不同要求;比如和,比如哈希表等;
while(判斷是否達到條件){ //如果達到條件左窗口就要收縮了;
res = Math.min(res, right-left+1); //只要滿足條件每次都要更新答案;
//更新窗口內數據;
result = result - nums[left]; //左窗口的值從結果去除;
left++; //左窗口移動;
}
right++; //只要不滿足條件右窗口移動;
}
其實,我們的右邊界就是在努力滿足條件,找到一個可行解,而我們的左邊界呢,想讓這個可行解更好一點,比如長度更短一下,是在尋找一個更好的最優解,但是很可能玩脫了不滿足條件。
在套模板的時候我們需要思考以下問題:
- 1.當移動right,即加入結果的時候,需要更新哪些數據;
- 2.到達什么條件時,窗口停止擴大,也就是右邊界停下來,開始左邊界移動,縮小窗口;
- 3.當移動left,即移出結果的時候,需要更新哪些數據;
- 4.要的最后答案應該在窗口擴大還是窗口縮小時更新;
4.樣例
5.體會
- 要能夠根據題目要求找到條件,因為條件是指揮我們移動左窗口還是右窗口的長官,不滿足條件,右窗口移動; 滿足條件,左窗口移動;
- 要始終清楚左右兩個邊界都是不會回退的,都是朝着最后走的,不可能會出現往回走的時候;
- 兩個窗口不會同時移動,每次只有兩個窗口中的一個移動。
- 換種思路:我們的窗口滑動其實就是一個雙端隊列。當不滿足條件時,數組元素依次從隊尾入隊;當滿足條件時,隊首元素出隊;所以有的時候去想象成一個隊列,可能會更好的理解;