開啟章節選單
滑動視窗
罕見簡介
滑動窗口(Sliding Window)是雙指針中的一種技巧,通常可以用來解決有單調性的連續子序列問題。
我們一樣會使用兩個指針,一個指向窗口的左端,另一個指向窗口的右端,然後根據題目的要求,我們會持續進行窗口的伸縮或移動。
例題
你有一個包含 個正整數的陣列,請找出最長的連續子序列,使得這個子序列的和不超過 。
首先,你可以先試著用最暴力的作法解決這個問題:
for (int l = 0; l < n; l++) { for (int r = l; r < n; r++) { // 現在我們要找出 l 和 r 之間的和 int sum = 0; for (int i = l; i <= r; i++) { sum += nums[i]; } // 更新答案 if (sum <= s) { ans = max(ans, r - l + 1); } } }
但是這個作法的時間複雜度是 ,對於 的情況,這個作法是不可接受的。
不過我們可以從中觀察到一個性質,就是當我們知道當前區間的值超過 時,無論如何都不能再擴大了,因為再怎麼擴大,區間的和只會更大。
這個單調性就是我們可以利用滑動視窗的原因,滑動窗口的題目通常都符合這樣的性質:「當我們知道當前區間的值不符合要求時,更大/更小的區間一定也不符合」。
一開始,我們的區間長這樣,沒有包含任何元素,也就是 :
接著嘗試擴大窗口,我們把右指針往右移動,發現目前區間的和還是小於 :
再擴大:
一直擴大到這邊的時候,可以發現區間和還沒超過 ,我們現在的最大的區間長度是 :
再擴大一次之後,我們發現區間的和已經超過 了:
這時候我們就要縮小窗口,把左指針往右移動:
區間和又小於 了,讓我們再擴大:
總之,我們會持續重複這個動作直到跑完整個陣列,這個邏輯簡單來說就是:
- 當區間和小於 時,我們就嘗試擴大窗口(右指針往右移動)
- 當區間和大於 時,我們就嘗試縮小窗口(左指針往右移動)
應該很容易估計時間複雜度是 ,雖然有兩層迴圈,但是每個元素最多只會被訪問一次,所以根據 均攤分析,這個作法的時間複雜度是 。
int sum = 0; for (int l = 0, r = 0; r < n; r++) { // 現在我要找出 l 和 r 之間的和 sum += nums[r]; // 如果 sum 超過 s,我們就要縮小窗口 while (sum > s) { sum -= nums[l]; l++; } // 更新答案 ans = max(ans, r - l + 1); }