玩转算法(五)——数组(滑动窗口)

滑动窗口的思路

思路一:暴力解法,但是通常会运行超时。

思路二:下方4个题都可以采用滑动窗口。我的滑动窗口开始默认是 [ left,right ] ,左闭右闭。所以开始left = 0,right = -1,不断的去判断索引为right+1所对应的值(每次要判断right+1是否超出数组)。

思路其实很简单:

  • 达不到条件right就一直递增。
  • 每次循环都会进行条件判定,达到条件后记录一下索引。
  • 然后left递增,使整体达不到条件。
  • 然后right再递增,使达到条件,记录索引,一直循环。
  • left遍历到数组的末尾,结束循环。

每个题都有不同的条件,所以,最重要的是怎么去定义这个条件,这是一个难点。

leetcode相关题目

209. 长度最小的子数组(中等)

209. 长度最小的子数组

这个题的条件比较简单,sum < s 时 right 增加,sum >= s 时 记录条件,left 减少。

这个题的条件就是sum。

res的数字没有改动,说明没有符合条件的数据或者nums数组为空。

public int minSubArrayLen2(int s, int[] nums) {
	int i = 0, j = -1, res = nums.length + 1;
	int sum = 0;
	
	while(i < nums.length) {
		if(j + 1 < nums.length && sum < s)
			sum += nums[++j];
		else
			sum -= nums[i++];
		
		if(sum >= s)
			if(j - i + 1 < res)
				res = j - i + 1;   			
	}
	
	if(res == nums.length + 1)
		return 0;
	return res;
}

3. 无重复字符的最长子串(中等)

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

开辟空间的小技巧:空格是32,0-9的ASCII码为48-57,A-Z的ASCII码为65-90,a-z的ASCII码为97-122。

  • 如果题目说了字符串在a-z之间,不包含空字符串。则int[] frep = new int[26] 就能够存储。frep[str.charAt(i) - ‘a’],索引从0到25。
  • 包含空格,则int[] frep = new int[100] 就能够存储。

要判断 right+1 所对应的值得频率是否为0,不为0,则被卡住,直到 left 增加使right+1 所对应的值得频率为0。

这个题的条件就是单个字符的频率是否为0。

public int lengthOfLongestSubstring(String s) {
	int i = 0, j = -1, res = 0;
	int[] frep = new int[100];
	
	while(i < s.length()) {
		if(j + 1 < s.length() && frep[s.charAt(j + 1) - ' '] == 0)
			frep[s.charAt(++j) - ' ']++;
		else 
			frep[s.charAt(i++) - ' ']--; 
		
		if(j - i + 1 > res)
			res = j - i + 1;
	}
	return res;
}

438. 找到字符串中所有字母异位词(中等)

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

key-value的小技巧

  • 数组索引和对应的值。不过开辟数组时,一些不需要的key的默认值也都为0。
  • hashmap集合。可以不用一些没有用到的key,而且可以方便的调用api。

思路一:与上题相似,上题是每个字母频率只能一次,这题根据给定的字符串,来计算频率。

思路二:与下题相似,采用计数器的方式来判断。

public List<Integer> findAnagrams(String s, String p) {
    int i = 0, j = -1;
    List<Integer> list = new ArrayList<Integer>();

    int[] num1 = new int[26];
    for(int m = 0; m < p.length(); m++) {
        num1[p.charAt(m) - 'a']++;
    }

    while(i < s.length()) {
        if(j + 1 < s.length() && num1[s.charAt(j + 1) - 'a'] > 0)
            num1[s.charAt(++j) - 'a']--;
        else
            num1[s.charAt(i++) - 'a']++;

        if(j - i + 1 == p.length())
            list.add(i);
    }
    return list;
}

76. 最小覆盖子串(困难)

76. 最小覆盖子串

思路:使用map集合记录字符串 t 各个字符的频率,使用一个count计数器,一直向前寻找字符串的 t 中的字符,找到一个,当key对应的value大于0时,value就减一,count就加一,直到map集合中的各个key对应的value都为0。

如何表明map集合中的各个key对应的value都为0了呢?答案是 t 的长度等于count。并表明这个 left 和 right的区间内,包含了字符串 t 。

然后 right 值卡住了,left 值向右移动,使count == t.length() 这个条件破坏掉,当count变成了count-1时,right 就可以继续向前移动。

所以这个题的条件就是count计数器,就相当于各个字符的频率总数。

public static String minWindow(String s, String t) {
    int l = 0, r = -1, count = 0;
    String res = "";
    int len = s.length();

    int[] num1 = new int[100];
    for(int m = 0; m < t.length(); m++) {
        num1[t.charAt(m) - 'A']++;
    }

    while(l < s.length()) {
        if(r + 1 < s.length() && count < t.length()) {
            r++;
            if (num1[s.charAt(r) - 'A'] > 0){
                count++;
            }
            num1[s.charAt(r) - 'A']--;
        }
        else {
            if (num1[s.charAt(l) - 'A'] == 0)
                count--;
            num1[s.charAt(l) - 'A']++;
            l++;
        }
        if(count == t.length()){
            if(r - l + 1 <= len){
                len = r - l + 1;
                res = s.substring(l, r + 1) ;
            }
        }
    }
    return res;
}

参考博客:https://blog.csdn.net/qq_41231926/article/details/81427851

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值