滑动窗口算法总结

滑动窗口算法一般需要用到双指针来进行解决,另外一类需要用到特殊的数据结构如Map(计数),队列(大小顺序记录)等。核心在于如何控制窗口的移动,左右指针什么时候动,什么时候不动,这个是关键。

最小覆盖子串

给你一个字符串 S、一个字符串 T,请在字符串 S 里面找出:包含 T 所有字符的最小子串。
示例:

输入: S = "ADOBECODEBANC", T = "ABC"
输出: "BANC"

说明:
如果 S 中不存这样的子串,则返回空字符串 “”。
如果 S 中存在这样的子串,我们保证它是唯一的答案。
思路:

  1. 首先在S串上构造一个窗口,初始化左右指针为left=right=0;
  2. 通过不断移动right,使[left,right]窗口符合规则
  3. 这个时候,停止移动right指针,开始移动left,但是每次移动都需要去判断一下是否符合条件
  4. 重复2,3步,知道right到末尾。

可抽象为伪代码

		List s;
        List<Integer> window;
        Integer left = 0, right = 0;
        
        while (right<length){
            window.add(s[right]);
            right++;
            while (window 符合要求){
                //更新结果
                update(window);
                //移动left指针
                window.remove(s[left]);
                left++;
            }
       }

通过以上可知 窗口本质就是个数组,重点在定义什么样的窗口符合要求,和如何去更新结果。
根据题意,我们可以定义两个Map分别计数符合要求的窗口和目前窗口的计数,更新的结果即为最小符合做的字符串是什么。

public String minWindow(String s, String t) {

        int left = 0, right = 0;

        Integer matchSize = 0;
        Map<Character, Integer> window = new HashMap<>();
        Map<Character, Integer> need = new HashMap<>();
        for (char c : t.toCharArray()) {
            need.put(c, need.get(c) != null ? need.get(c) + 1 : 1);
        }
        int minLength = Integer.MAX_VALUE;
        int start = 0;
        while (right < s.length()) {
            char c1 = s.charAt(right);

            window.put(c1, window.get(c1) != null ? window.get(c1) + 1 : 1);
            if (need.get(c1) != null) {
                if (window.get(c1).equals(need.get(c1))) {
                    matchSize++;
                }
            }
            right++;

            while (matchSize == need.size()) {

                if (right - left < minLength) {
                    start = left;
                    minLength = right - left;
                }

                // 重新计算窗口值
                char c2 = s.charAt(left);
                if (need.get(c2) != null) {
                    window.put(c2, window.get(c2)- 1);
                    if (window.get(c2) < need.get(c2)) {
                        matchSize--;
                    }
                }
                left++;

            }
        }
        return minLength == Integer.MAX_VALUE ? "" : s.substring(start, start+minLength);

    }

找到字符串中所有字母异位词

和上题一样,就是在符合条件的时候,更新结果的时候,判断条件改为了right-left=字串长度。

public List<Integer> findAnagrams(String s, String p) {

        int left = 0, right = 0;

        List<Integer> res = new ArrayList<>();

        Map<Character, Integer> window = new HashMap<>();
        Map<Character, Integer> need = new HashMap<>();
        for (char c : p.toCharArray()) {
            need.put(c, need.get(c) != null ? need.get(c) + 1 : 1);
        }
        Integer match = 0;

        while (right < s.length()) {
            char c1 = s.charAt(right);
            window.put(c1, window.get(c1) != null ? window.get(c1) + 1 : 1);
            if (window.get(c1).equals(need.get(c1))) {
                match++;
            }
            right++;

            while (match == need.size()) {

                if (right - left == p.length()) {
                    res.add(left);
                }

                char c2 = s.charAt(left);
                window.put(c2, window.get(c2) - 1);
                if (need.get(c2) != null) {
                    if (window.get(c2) < need.get(c2)) {
                        match--;
                    }
                }

                left++;
            }

        }
        return res;
    }

无重复字符的最长子串

 public int lengthOfLongestSubstring(String s) {

        int left=0,right=0;

        Map<Character,Integer> window=new HashMap<>();

        int max=Integer.MIN_VALUE;

        while (right<s.length()){
            char c1=s.charAt(right);
            window.put(c1,window.get(c1)!=null?window.get(c1)+1:1);
            right++;

            while (window.get(c1)>1){
                char c2=s.charAt(left);
                window.put(c2,window.get(c2)-1);
                left++;
            }
            if (right-left>max){
                max=right-left;
            }
        }

        return max==Integer.MIN_VALUE?0:max;
    }

滑动窗口最大值

    LinkedList<Integer> linkedList = new LinkedList<>();

    public int[] maxSlidingWindow(int[] nums, int k) {

        int n = nums.length;
        int[] res = new int[n - k + 1];
        int left = 0;
        int right = 0;
        int count = 0;
        while (right < n) {

            clean_queue(nums,right,k);
            linkedList.add(right);
            right++;
            while (right-left >= k && right <= n) {

                res[count++] = nums[linkedList.getFirst()];
                left++;
            }

        }
        return res;

    }

    public void clean_queue(int[] nums, int i, int k) {
        if (!linkedList.isEmpty() && i - k == linkedList.getFirst()) {
            linkedList.removeFirst();
        }

        while (!linkedList.isEmpty() && nums[i] > nums[linkedList.getLast()]) {
            linkedList.removeLast();
        }

    }```

通过总结,可抽象出模版

```java
int left = 0, right = 0;

while (right < s.size()) {
	//添加窗口
    window.add(s[right]);
    right++;
    
    while (符合窗口规则) {
        //更新值
        window.remove(s[left]);
        left++;
    }
}

你可能感兴趣的:(每天一道面试题,数据结构与算法)