LeetCode技巧篇(二)Two points for Sliding Window 滑動窗口中的雙指針

介紹

雙指針是LeetCode裏面非常實用並且常用的一種技巧,而且有應用的範圍也很廣。比如二分法(binary search)也可以看成是左右兩個指針縮小搜索範圍,確定最終結果快慢指針確定鏈表是不是含有環;等等。不過這裏筆者介紹用在滑動窗口結合雙指針的技巧。下面先引入一個簡單的例子:LeetCode 3,難度中等,要求找到無重複字符的最長子串。
在這裏插入圖片描述
那麼對於上述的字符串來說最長的無重複字符的子串是abc(其中的一種),所以長度是3。那麼最直觀的就是暴力搜索,用一個map或者set存儲遍歷到的字符,如果出現重複的話就退出,然後比較長度。
但是這樣的解法顯然時間複雜度太高。我們可以嘗試構建一個窗口,判斷這個窗口是否滿足要求;如果滿足要求,那麼縮小窗口;如果不滿足要求,繼續往前滑動。重複以上操作,得到最終的結果。
在這裏插入圖片描述
那麼這類問題的核心代碼就是:

//定義左右指針
int left = 0, right = 0;
while(right<s.length()){
	//窗口添加元素,且right指針往右前進
	while(meet){
		//窗口裏面的元素減少,且left指針向右前進
	}
}

實例

可以用滑動窗口雙指針解決的leetcode問題有:LeetCode 3,LeetCode 713, LeetCode 438。
LeetCode 3的代碼如下:

    public int lengthOfLongestSubstring(String s) {
        char ch[] = s.toCharArray();
        HashMap<Character, Integer> map = new HashMap();
        int left=0, right=0,ans=0;
        while(right<s.length()){
            char c1 = ch[right++];
            map.put(c1, map.getOrDefault(c1,0)+1);
            while(map.get(c1)>1){
                char c2 = ch[left];
                map.put(c2, map.get(c2)-1);
                left++;
            }
            ans = Math.max(ans,right-left);
        }
        return ans;
    }

LeetCode 713,這題比較簡單,要求找到乘積小於k的連續子數組的個數。

    public int numSubarrayProductLessThanK(int[] nums, int k) {
        if(k<=1) return 0; 
        int left =0, len =0,pro=1;
        for(int j=0;j<nums.length;j++){
            pro *=nums[j];
            while(pro>=k) pro/=nums[left++];
            len+=j-left+1;
        }
        return len;
    }

LeetCode 438,要求找到異位詞的起始位置。所謂異位詞,就是和模板字符串裏出現的字符都一樣,並且可以對應位置不同。比如abc的異位詞可以是cba,cab等。

    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> list = new ArrayList();
        int[] arrS = new int[26];
        int[] arrP = new int[26];
        int count=0;
        for(char c:p.toCharArray()){
            arrP[c-'a']++;
            if(arrP[c-'a']==1) count++;
        }
        int left=0, right=0,match=0;
        while(right<s.length()){
            char c1 = s.charAt(right);
            if(arrP[c1-'a']>0){
                arrS[c1-'a']++;
                if(arrS[c1-'a']==arrP[c1-'a']) match++;
            }
            right++;
            while(match==count){
                if(right-left==p.length()){
                    list.add(left);
                }
                char c2 = s.charAt(left);
                if(arrP[c2-'a']>0){
                    arrS[c2-'a']--;
                    if(arrS[c2-'a']<arrP[c2-'a']) match--;
                }
                left++;
            }
        }
        return list;
    }

總結

當題目中出現字符串匹配連續子序列等字樣的時候,如果只是簡單的使用暴力解法,那麼時間複雜度一般都會超過O(n2n^2),這是可以考慮使用滑動窗口+雙指針解決問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章