目录
前言
题目描述:给定一个含有 n 个正整数的数组和一个正整数 target。找出该数组中满足其和 >= target 的长度最小的连续子数组 [nums_l, nums_l+1, ..., nums_r-1, nums_r],并返回其长度。如果不存在符合条件的子数组,返回 0。
题目链接:209. 长度最小的子数组 - 力扣(Leetcode)。
一、暴力解法
#include <limits.h>
int minSubArrayLen(int target, int* nums, int numsSize)
{
int i = 0;
int j = 0;
int sum = 0;
int subLength = 0;
int result = INT_MAX; // 或者 int result = numsSize + 1;
for (i = 0; i < numsSize; i++) // 子数组的起点为 i
{
sum = 0; // 子数组的元素和
for (j = i; j < numsSize; j++) // 子数组的终点为 j
{
sum += nums[j];
if (sum >= target)
{
subLength = j - i + 1; // 求满足条件的子数组的长度
result = subLength < result ? subLength : result; // 求较小值
break; // 退出内层循环
}
}
}
return result == INT_MAX ? 0 : result;
}
二、滑动窗口
滑动窗口算法思想是非常重要的一种思想,可以用来解决数组、字符串的子元素问题。它可以将嵌套循环的问题,转换为单层循环问题,降低时间复杂度,提高效率。
算法思路:
-
使用双指针中的左右指针技巧,初始化 left = right = 0,把索引闭区间 [left, right] 称为一个窗口。
-
先不断地增加 right 指针,扩大窗口 [left, right],直到窗口符合要求。
-
停止增加 right,转而不断增加 left 指针,缩小窗口 [left, right],直到窗口中的子数组(或子字符串)不再符合要求。同时,每次增加 left,我们都要更新一轮结果。
-
重复第 2 和第 3 步,直到 right 到达尽头。
int minSubArrayLen(int target, int* nums, int numsSize)
{
int left = 0;
int right = 0;
int sum = 0;
int subLength = 0;
int result = numsSize + 1;
for (right = 0; right < numsSize; right++)
{
sum += nums[right];
while (sum >= target) // 不断地增加 right 指针,扩大窗口 [left, right],直到窗口符合要求
{
subLength = right - left + 1;
result = subLength < result ? subLength : result;
sum -= nums[left++]; // 不断地增加 left 指针,缩小窗口 [left, right],直到窗口不符合要求
}
}
return result <= numsSize ? result : 0;
}
示例:
输入:target = 7,nums = {2, 3, 1, 2, 4, 3}
输出:2
过程如下图所示:
left = 0 时的最优解为 right = 3;
left 为 1 时的最优解为 right = 4;
left 为 2 时的最优解为 right = 4;
left 为 3 时的最优解为 right = 5;
left 为 4 时的最优解为 right = 5;
left 为 5 时没有解。