常见面试算法题之滑动窗口

177 阅读1分钟

算法思想

滑动窗⼝算法的思路是这样:

  1. 我们在字符串 S 中使⽤双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为⼀个「窗⼝」。

  2. 我们先不断地增加 right 指针扩⼤窗⼝ [left, right],直到窗⼝中的字符串符合要求(包含了 T 中的所有字符)。

  3. 此时,我们停⽌增加 right,转⽽不断增加 left 指针缩⼩窗⼝ [left, right],直到窗⼝中的字符串不再符合要求(不包含 T 中的所有字符了)。 同时,每次增加 left,我们都要更新⼀轮结果。

  4. 重复第 2 和第 3 步,直到 right 到达字符串 S 的尽头。

int left = 0, right = 0; 
while (right < s.size()) { 
  window.add(s[right]);
  right++; 
  while (valid) {
    window.remove(s[left]);
    left++;
  } 
}

1.最小覆盖子串

leetcode-cn.com/problems/mi…

class Solution {
    public String minWindow(String s, String t) {
        //1. 用一个map来记录需要多少字符,每个字符需要多少次
        Map<Character , Integer> need = new HashMap<>();
        for(char c : t.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);
        //2. 用一个窗口来表示是否满足条件
        Map<Character , Integer> win = new HashMap<>();
        int l = 0 , r = 0 , count = 0;
        int len = Integer.MAX_VALUE , start = 0 , end = 0; //记录长度和起始终止位置
        while(r < s.length()){
            char a = s.charAt(r);
            if(need.containsKey(a)){
                win.put(a , win.getOrDefault(a , 0 ) + 1);
                if(win.get(a).intValue() == need.get(a).intValue()) count++;
            }
            r++;
            while(count == need.size()){ //当满足条件的时候
                if(len > r-l){
                    len = r - l;
                    start = l;
                    end = r;
                }
                char b = s.charAt(l);
                if(need.containsKey(b)){
                    win.put(b , win.getOrDefault(b,0)-1);
                    if(win.get(b) < need.get(b)) count--;
                }
                l++;
            }
        }
        return s.substring(start , end);
    }
}

2.无重复字符的最长子串

leetcode-cn.com/problems/lo…

class Solution {
    public int lengthOfLongestSubstring(String s) {
        int n = s.length();
        if(n == 0 || n == 1) return n;
        Map<Character , Integer> map = new HashMap<>();
        int res = 0;
        for(int i = 0 , j = 0 ; i < n ; i++){
            char c = s.charAt(i);
            if(map.containsKey(c)){
                j = Math.max(j , map.get(c));
            }
            map.put(c , i+1);
            res = Math.max(res , i+1 - j);
            
        }
        return res;
    }
}

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

(leetcode-cn.com/problems/fi…)

class Solution {
    public List<Integer> findAnagrams(String s, String p) {
        List<Integer> res = new ArrayList<>();
        Map<Character,Integer> need = new HashMap<>();
        for(char c : p.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);

        Map<Character,Integer> win = new HashMap<>();
        int count = 0 , l = 0 , r = 0;
        while(r < s.length()){
            char a = s.charAt(r);
            if(need.containsKey(a)){
                win.put(a , win.getOrDefault(a , 0)+1);
                if(win.get(a).intValue() == need.get(a).intValue()) count++;
            }

            r++;
            while(count == need.size()){
                if(r - l  == p.length()){ //如果满足条件,加入到res中
                    res.add(l);
                }
                char b = s.charAt(l);
                if(need.containsKey(b)){
                    win.put(b , win.getOrDefault(b ,0)-1);
                    if(win.get(b) < need.get(b)) count--;
                }
                l++;
            }
        }
        return res;
    }
}

4.判断字符串是否包含排列

leetcode-cn.com/problems/pe…

class Solution {
    public boolean checkInclusion(String s, String p) {
        Map<Character , Integer> need = new HashMap<>();
        for(char c : s.toCharArray()) need.put(c , need.getOrDefault(c , 0) + 1);

        Map<Character,Integer> win = new HashMap<>();
        int l = 0 , r = 0;
        int count = 0;
        while(r < p.length()){
            char a = p.charAt(r);
            if(need.containsKey(a)){
                win.put(a , win.getOrDefault(a , 0) + 1);
                if(win.get(a).intValue() == need.get(a).intValue()) count++;
            }
            r++;
            while(r - l >= s.length()){
                if(count == need.size()) return true;
                char b = p.charAt(l);
                if(need.containsKey(b)){
                    if(win.get(b) == need.get(b)) count--;
                    win.put(b , win.getOrDefault(b,0)-1);
                }
                l++;
            }
        }
        return false;
    }
}

固定大小的窗口问题

1.滑动窗口最大值

leetcode-cn.com/problems/sl…

class Solution {
    public int[] maxSlidingWindow(int[] nums, int k) {
        int n = nums.length;
         if(n < 2) return nums;
        int[] res = new int[n-k+1];
        //双向链表,队列中数组位置的数按从大到小排序
        LinkedList<Integer> list = new LinkedList<>();
        for(int i = 0 ; i < n ; i++){
            //// 保证从大到小 如果前面数小 弹出
            while(!list.isEmpty() && nums[list.peekLast()] <= nums[i]) list.pollLast();
            list.addLast(i);
            // 初始化窗口 等到窗口长度为k时 下次移动在删除过期数值
            if(list.peek() <= i-k) list.poll();
            if(i - k + 1 >= 0) res[i-k+1] = nums[list.peek()];
        }
        return res;
    }
}

不固定大小的窗口问题