美文网首页
玩转算法面试之数组(三)

玩转算法面试之数组(三)

作者: 爱笑的云里看梦 | 来源:发表于2018-03-30 17:03 被阅读44次

看一下上一篇留下的那个题目:

LeetCode 209:Minimum Size Subarray Sum

给定一个整型数组和一个数字S,找到数组中的一个连续子数组,使得连续子数组的数值之和sum>=S,返回此最短的连续子数组的长度。

 - 如给定数组[2,3,1,2,4,3],S=7
 - 答案为[4,3],返回2. 如果无解,返回0.

首先,如果一开始没有想到好的解法,那么可以考虑采用暴力解法:即遍历所有的连续子数组[i...j],计算其sum和,验证sum>=S。
使用暴力解法的时间复杂度为O(N3)。
但是暴力解法包含了大量的重复计算,它会对已经计算过的结果,在下一次遍历的时候又再进行一次计算。
为了解决这个问题,可以这样考虑:

  • 首先使用i和j分别指向一段数组中的一段数据,即nums[i]~nums[j]。
  • 如果nums[i]~nums[j]这个数据段里的和小于S,那么就把j的值+1,即向后多看一个数据,检查是否仍然小于S,如果小于,则继续这么做。
  • 如果nums[i]nums[j]这个数据段的和大于S了,则记下此时ij的元素个数Min并且与之前记录的最短长度比对后取最小值,然后把i的值+1,即将数据段缩小一位,再进行检查。直到i的值到数组末尾为止。
  • 这样最后得到的Min即为最短的连续子数组的长度。

在这个解法中,由i ~ j维持了一个不断变化的滑动窗口,时间复杂度为O(N)。利用这种方法,可以求解很多问题。下面是此题的具体实现:

int minSubArrayLen(int s, vector<int>& nums) {
        // l~r即为我们要构造的滑动窗口,r=-1表示一开始还没有此窗口
        int l = 0;
        int r = -1;
        int sum = 0;
        int length = nums.size() + 1;
        while(l < nums.size()) {
            //别忘记判断r数组越界
            if(sum < s && r < nums.size() - 1) {
                sum += nums[++r];
            } else {
                sum -= nums[l++];
            }
            if(sum >= s) {
                length = min(length, r-l+1);
            }
        }
        if(length == nums.size() + 1)
            return 0;
        else
            return length;
    }

掌握了滑动窗口之后,再来看一下LeetCode上的另外一道与之相关的题目。
LeetCode 3 Longest SubString Without Repeating Characters
在字符串中寻找没有重复字母的最长子串

- 如“abcabcab”,返回3,abc
- 如“pwwkew”,返回3,wke/kew

使用滑动窗口解此题目的方法如下:

  • 使用i和j构造一个滑动窗口
  • 对于每次窗口增大(即j++)时,判断进入窗口的元素是否在滑动窗口内出现过,如果没出现,则将窗口增大一位。
  • 在增大窗口时,如果窗口里已经有此元素了,那么就把i的值-1,即缩小一下窗口,直到窗口内没有出现此元素。
  • 每次记录滑动窗口最大到过多少,然后返回即可
    在这里,有一个小细节,如何判断即将进入的元素是否在滑动窗口中出现过?总不能一一比较吧。
    这里有一个比较简单的思路是设置一个字符数组freq[256],存储所有的字符,它的值代表每个字符出现的次数。算法代码如下:
int lengthOfLongestSubstring(string s) {
        if(s.size() == 0) {
            return 0;
        }
        int freq[256] = {0};
        int l = 0, r= -1;
        int res = 0;
        while (l < s.size()) {
              if(r+1 < s.size() && freq[s[r+1]] == 0) 
                         freq[s[++r]] ++;
              else
                         freq[s[l++]] -- ;
              res = max(res, r-l+1);
        }
    return res;
    }

对于这个看起来复杂的问题,解决的代码却很简单。关键在于思路是正确且清晰的,这样就能快速地找到解决办法。
下面是一些与之相关的题目可供练习,代码和思路照例会在下一篇中给出。大家有问题请给我留言评论。

LeetCode438 Find All Anagrams in a String
给定一个字符串S和一个非空字符串P,找出P中的所有是S的anagrams字符串的子串并返回这些子串的起始索引。

- 如S = "cbaebabacd",P="abc",返回[0,6]。
- 如S = "abab",P = "ba",返回[0,1,2]。

注:anagrams字符串指字母相同,顺序不同。

LeetCode76 Minimum Window SubString
给定一个字符串S和T,在S中寻找最短的子串,包含T中的所有字符。

- 如:S = "ADOBECODEBANC"; T = "ABC"
- 结果为"BANC"

注:这是一道难度为hard的题目。

至此,数组阶段就已经结束了。在后面的文章中,将会介绍查找这种面试中同样常见的问题。希望大家继续关注。

相关文章

网友评论

      本文标题:玩转算法面试之数组(三)

      本文链接:https://www.haomeiwen.com/subject/kjyicftx.html