滑動窗口(Sliding Window)算法介紹

前言

最近刷到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).bash

解法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)。在最差的狀況下,每一個字符將會被訪問兩次ui

解法3:優化的滑動窗口算法

上面的滑動窗口算法最多須要2n的步驟,但這實際上是能被優化爲只須要n步。咱們可使用HashMap定義字符到索引之間的映射,而後,當咱們發現子字符串中的重複字符時,能夠直接跳過遍歷過的字符了。spa

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)code

滑動窗口算法總結

  • 滑動窗口算法能夠用以解決數組/字符串的子元素問題
  • 滑動窗口算法能夠將嵌套的for循環問題,轉換爲單循環問題,下降時間複雜度
相關文章
相關標籤/搜索