数组 | LeetCode 209 长度最小的子数组

141 阅读2分钟

题目描述

给定一个含有 n 个正整数的数组和一个正整数 s ,找出该数组中满足其和 ≥ s 的长度最小的 连续 子数组,并返回其长度。如果不存在符合条件的子数组,返回 0。

本题思路

暴力解法

class Solution {
public:
    int minSubArrayLen(int s, vector<int>& nums) {
        int result = INT32_MAX; // 最终的结果
        int sum = 0; // 子序列的数值之和
        int subLength = 0; // 子序列的长度
        for (int i = 0; i < nums.size(); i++) { // 设置子序列起点为i
            sum = 0;
            for (int j = i; j < nums.size(); j++) { // 设置子序列终止位置为j
                sum += nums[j];
                if (sum >= s) { // 一旦发现子序列和超过了s,更新result
                    subLength = j - i + 1; // 取子序列的长度
                    result = result < subLength ? result : subLength;
                    break; // 因为我们是找符合条件最短的子序列,所以一旦符合条件就break
                }
            }
        }
        // 如果result没有被赋值的话,就返回0,说明没有符合条件的子序列
        return result == INT32_MAX ? 0 : result;
    }
};

代码是从代码随想录复制过来,没有自己打过。这需要反思,不能因为是暴力就忽视。

滑动窗口

class Solution {
public:
    int minSubArrayLen(int target, vector<int>& nums) { // 滑动窗口
        int sum = 0;
        int i = 0,j;
        int result = INT32_MAX;
        for (j = 0; j < nums.size(); j++) {
            sum += nums[j];
            while (sum >= target) {
                int s = j - i + 1;
                result = min(result, s);
                sum -= nums[i];
                i++;
            }
        }
        return result == INT32_MAX ? 0 : result;
    }
};

这个是自己纯手工打了很多遍的代码。

个人见解

j表示的是子串的末尾位置,i表示的是子串的起始位置。

第一理解

可能不太严谨个人理解,如有错误还望指出!

  1. 滑动窗口只枚举了最有可能的几个解,首先是以第一个字符为首的字符串开始寻找,直到总和大于目标.那么再往后找就以无意义,不再是最小子数组
  2. 记录当前的子数组长度
  3. 接着将滑动窗口起始指针向后移动(因为包含起始位置元素的子数组可能不是最小子数组,所以滑动窗口起始指针移动,比较其后的子数组是否为满足条件的子数组)
  4. 满足则更改记录的数据
  5. 如果没有更改原本的数据即为没有满足的条件

第二理解

例如 abcde 代表不同数字

外层循环 先不看i的移动

  1. a
  2. ab
  3. abc
  4. abcd
  5. abcde

前一次所遍历的字符串都为后一次的子串,假如bc为外层循环第三次得到的第一个结果,那么第四次i就无需再从abcd考虑,直接考虑bcd是否还有更短的子串。解释i不重置的问题,同样也是这题的主要思想