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

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

作者: 爱笑的云里看梦 | 来源:发表于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