前言
最近刷到leetCode裡面的一道演算法題,裡面有涉及到Sliding windowing演算法,因此寫一篇文章稍微總結一下
演算法題介紹
沒有重複字元的子字元的最大長度:給一個字串,獲得沒有重複字元的最長子字元的長度
例子:
輸入:"abcabcbb"
輸出:3
解釋:因為沒有重複字元的子字元是'abc',所以長度是3
解法1:暴力解法
public class Solution {//時間複雜度高O(n3)
public int lengthOfLongestSubstring(String s) {
int n = s.length();
int ans = 0;
//遍歷所有的子字串,記錄沒有重複字母的子字串的最大的長度
//獲取子字串時,使用兩個標籤,分別代表子字串的開頭和結尾
for (int i = 0; i < n; i++)
for (int j = i + 1; j <= n; j++)
//當子字串沒有重複字母時,ans記錄最大的長度
if (allUnique(s, i, j)) ans = Math.max(ans, j - i);
return ans;
}
//判斷該子字串是否有重複的字母
public boolean allUnique(String s, int start, int end) {
//HashSet實現了Set介面,它不允許集合中出現重複元素。
Set<Character> set = new HashSet<>();
for (int i = start; i < end; i++) {
Character ch = s.charAt(i);
if (set.contains(ch)) return false;
set.add(ch);
}
return true;
}
}
複製程式碼
分析
時間複雜度:O(n3).
解法2:滑動視窗演算法
通過使用HashSet作為一個滑動視窗,檢查一個字元是否已經存在於現有的子字元中只需要O(1).
滑動視窗經常作為一個抽象的概念來處理陣列/字串問題。視窗代表著一組資料/字串元素,通過開頭和結尾的索引來定義視窗。
public class Solution {//時間複雜度O(2n)
//滑動視窗演算法
public int lengthOfLongestSubstring(String s) {
int n = s.length();
Set<Character> set = new HashSet<>();
int ans = 0, i = 0, j = 0;
while (i < n && j < n) {//視窗的左邊是i,右邊是j,下列演算法將視窗的左右移動,擷取出其中一段
// try to extend the range [i, j]
if (!set.contains(s.charAt(j))){//如果set中不存在該字母,就將j+1,相當於視窗右邊向右移動一格,左邊不動
set.add(s.charAt(j++));
ans = Math.max(ans, j - i);//記錄目前存在過的最大的子字元長度
}
else {//如果set中存在該字母,則將視窗左邊向右移動一格,右邊不動,直到該視窗中不存在重複的字元
set.remove(s.charAt(i++));
}
}
return ans;
}
}
複製程式碼
分析
時間複雜度:O(2n)。在最差的情況下,每個字元將會被訪問兩次
解法3:優化的滑動視窗演算法
上面的滑動視窗演算法最多需要2n的步驟,但這其實是能被優化為只需要n步。我們可以使用HashMap定義字元到索引之間的對映,然後,當我們發現子字串中的重複字元時,可以直接跳過遍歷過的字元了。
public class Solution {//時間複雜度o(n)
public int lengthOfLongestSubstring(String s) {
int n = s.length(), ans = 0;
//使用hashmap記錄遍歷過的字元的索引,當發現重複的字元時,可以將視窗的左邊直接跳到該重複字元的索引處
Map<Character, Integer> map = new HashMap<>(); // current index of character
// try to extend the range [i, j]
for (int j = 0, i = 0; j < n; j++) {//j負責向右邊遍歷,i根據重複字元的情況進行調整
if (map.containsKey(s.charAt(j))) {//當發現重複的字元時,將字元的索引與視窗的左邊進行對比,將視窗的左邊直接跳到該重複字元的索引處
i = Math.max(map.get(s.charAt(j)), i);
}
//記錄子字串的最大的長度
ans = Math.max(ans, j - i + 1);
//map記錄第一次遍歷到key時的索引位置,j+1,保證i跳到不包含重複字母的位置
map.put(s.charAt(j), j + 1);
}
return ans;
}
}
複製程式碼
分析
時間複雜度:O(n)
滑動視窗演算法總結
- 滑動視窗演算法可以用以解決陣列/字串的子元素問題
- 滑動視窗演算法可以將巢狀的for迴圈問題,轉換為單迴圈問題,降低時間複雜度