我們學習過計算機網路都知道為了避免擁塞發生,在網路傳輸時有滑動視窗協議控制傳輸時流量。該協議允許傳送方在停止並等待確認前傳送多個資料分組。由於傳送方不必每發乙個分組就停下來等待確認,因此該協議可以加速資料的傳輸,提高網路吞吐量。這個跟我們今天說的滑動視窗演算法是乙個原理。
該演算法的作用就是將我們多層巢狀的迴圈語句根據區域性最優解來轉換為單個的迴圈語句,從而減少時間複雜性。
下面我們通過leetcode 上的一道題來看看滑動視窗究竟該如何使用
給定乙個字串,請你找出其中不含有重複字元的 最長子串 的長度。
示例 1:
輸入: 「abcabcbb」示例 2:輸出: 3
解釋: 因為無重複字元的最長子串是 「abc」,所以其長度為 3。
輸入: 「bbbbb」示例 3:輸出: 1
解釋: 因為無重複字元的最長子串是 「b」,所以其長度為 1。
輸入: 「pwwkew」這道題目我們需要找到字串中的最長的子串,普通的暴力解法是通過for迴圈遍歷給定字串的所有子串,判斷子串中有沒有重複的字元,如果沒有則找出最長的。輸出: 3
解釋: 因為無重複字元的最長子串是 「wke」,所以其長度為 3。
請注意,你的答案必須是 子串 的長度,「pwke」 是乙個子串行,不是子串。
但是上面的演算法我們時間複雜度為o(n^3)
。那麼有沒有更好的演算法呢,當然是有的,那就是滑動視窗演算法。通過使用hashset
作為滑動視窗,我們可以用o(1)
的時間來完成對字元是否在當前的子字串中的檢查。
滑動視窗
是陣列/字串問題中常用的抽象概念。視窗通常是在陣列/字串中由開始和結束索引定義的一系列元素的集合,即[left,right)(左閉,右開)。而滑動視窗是可以將兩個邊界向某一方向「滑動」的視窗。例如,我們將[left,right)向右滑動1個元素,則它將變為[left+1,right+1)(左閉,右開)。我們使用hashset
將字元儲存在當前視窗[left,right)(最初left=right)中。然後我們向右側滑動索引right,如果它不在hashset
中,我們會繼續滑動 right。直到s[right]已經存在於hashset
中。此時,我們找到的沒有重複字元的最長子字串將會以索引left
開頭。如果我們對所有的leift
這樣做,就可以得到答案。
public int lengthoflongestsubstring(string s)
else
}return ans;
}
這裡時間複雜度為 o(2n)=o(n),空間複雜度為o(min(m,n))。
這裡我們還可以將演算法優化,將字串中每個字元和索引形成對映關係。舉個例子,給定字串「abcabcbb」,這裡我們依次按照上述演算法執行,我們到第四個字元也就是right為3時,我們left可以跳過前面的0123,直接left和right為4,從4開始再繼續找。
public int lengthoflongestsubstring(string s)
ans = math.max(ans,right-left+1);
map.put(s.charat(right),right+1);
}return ans;
}
從上面的解題我們可以明顯的發現,滑動視窗演算法的的時間複雜度為線性的(o(n)),我們可以用此演算法來查詢最大/最小k-子串行,xor,乘積,總和等一些列問題。
無重複字元的最長子串
滑動視窗演算法
在letecode程式設計中經常會碰到一類尋找最長序列的問題,這個時候都可以採用滑動視窗的演算法 滑動視窗就是控制乙個雙指標left,right,沒有達到極限條件的時候right指標,操作臨界條件 就讓left指標,然後更新這個最長序列。比如下面的的問題 問題一 給你兩個長度相同的字串,s 和 t。...
滑動視窗演算法
滑動視窗演算法可以用以解決陣列 字串的子元素問題,它可以將巢狀的迴圈問題,轉換為單迴圈問題,降低時間複雜度。給定乙個字串,請你找出其中不含有重複字元的最長子串的長度。輸入 s abcabcbb 輸出 3 解釋 因為無重複字元的最長子串是 abc 所以其長度為 3。通過不斷滑動視窗的大小,判斷視窗中元...
滑動視窗演算法 演算法 滑動視窗 二
演算法 這算是滑動視窗的另外乙個典型題目,在資料量比較少的時候,可以直接採用暴力法解決 不過資料量比較大的時候,我們就需要想辦法解決視窗裡面最大值的思路,這裡我們採用雙端佇列queue來實現,借助 queue來儲存前面計算過的最大值資訊。題目 解法1 暴力解法 按照 視窗大小,從頭到尾依次遍歷,將每...