什么是「滑動窗口算法」(sliding window algorithm),有哪些應用場景?


今天是算法數據結構專題的第2篇文章,我們一起來學習一下「滑動窗口算法」。

前言

最近刷到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循環問題,轉換為單循環問題,降低時間復雜度

文章出處


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM