滑動窗口算法思路


關於雙指針的方法,可能大家並不陌生,而滑動窗口算法,其實算是雙指針的一種實現方式,主要用於解決子串問題。並且在leetCode上起碼有10道以上的滑動窗口應用題目,難度均在middle和hard。因此,我本文也致力於闡述自己的想法,供大家互相學習。

 

首先滑動窗口算法,顧名思義,就是要維護一個窗口,然后通過不斷的滑動,最終跟新答案。下面是我從“Labuladuo算法小抄”上看到的偽代碼:

int left = 0, right = 0;

while (right < s.size()) {
    // 增大窗口
    window.add(s[right]);
    right++;

    while (window needs shrink) {
        // 縮小窗口
        window.remove(s[left]);
        left++;
    }
}

 

這個算法的復雜度是O(N),比暴力算法高效許多。基本思路也就是用左右指針去維護窗口,下面是我自己一般喜歡用的代碼寫法:

 

    public void SlidingWindow(String s,String t){
        Map<Character,Integer> need=new HashMap<Character,Integer>();   //最終目的子串
        Map<Character,Integer> Window=new HashMap<Character,Integer>(); //目前窗口集合 char [] tCharArray=t.toCharArray();
     char [] sCharArray=s.toCharArray(); 
for(char c : sCharArray) need.put(c, need.getOrDefault(c, 0) + 1);
int left=0,right=0; int valid=0; while(right<s.length()) { char c=s.charAt(right); //c是將要移入窗口的字符 right++; //右移窗口 .... //對窗口中的數據進行更新 System.out.println("left為:"+left+" right為:"+right); while (window needs shrink)//判斷左側窗口是否要收縮 { char d=s.charAt(left); //d是將移出的窗口的字符 left++; //左移窗口 .... //進行窗口中數據系列更新 } }

 

看一道LeetCode上的hard題。

 

 這道題暴力只是理論上可以解決的,但是,一定會超時。比如下面的寫法:

for (int i = 0; i < s.size(); i++)
    for (int j = i + 1; j < s.size(); j++)
        if s[i:j] 包含 t 的所有字母:
            更新答案

 而通過滑動窗口算法,即可大大降低復雜度和運算時間:

    public String minWindow(String s, String t) {
        Map<Character,Integer> need=new HashMap<>();    //子串窗口
        Map<Character,Integer> window=new HashMap<>();   //滑動窗口
        char [] sCharArrays=s.toCharArray();
        char [] tCharArrays=t.toCharArray();
        for(char c:tCharArrays){
            need.put(c,need.getOrDefault(c,0)+1);    //用hashmap記錄出現字母的次數
        }

        int left=0,right=0;
        int valid=0;
        int length=Integer.MAX_VALUE;
        int start=0;

        while(right<s.length())              //右指針滑動
        {
            char c=sCharArrays[right];          //right划過窗口的元素
            right++;
            if(need.containsKey(c))        //如果是是子串的組成字母之一,加入滑動窗口
            {
                window.put(c,window.getOrDefault(c,0)+1);  
                if(window.get(c).equals(need.get(c)))       //如果該字母數目在滑動窗口和子串窗口中相同,valid++
                    valid++;
            }

        
            while(valid==need.size())           //當所有的字母在window和need中出現次數相同,開始收縮窗口
            {   
                if(right-left<length)
                {
                    length=right-left;
                    start=left;
                }
                char d=sCharArrays[left];           //left划出的元素
                left++;
                if(need.containsKey(d)){                    
                    if(window.get(d).equals(need.get(d)))   //注意:在這里只能寫.equals(),不能寫==
                        valid--;                            //  因為這里是Integer對象之間的比價,如果超過
                    window.put(d,window.get(d)-1);          //-128-127會重新new一個對象使==報錯。(上面的equals同)
                }
                    
            }
        }
        if(length==Integer.MAX_VALUE) return "";
        return s.substring(start,start+length);
        
    }

最后,滑動窗口算是雙指針里比較復雜的一種情況,但如果掌握大概思路,許多子串問題都能順利的解決。

 

 


免責聲明!

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



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