这段代码是一个 C++ 程序,用于解决“最小连续子数组和”问题。给定一个整数数组 a
和一个整数 S
,程序的目标是找到数组中长度最短的连续子数组,其元素之和至少为 S
。
下面是代码块的逐行分析:
#include <iostream> // 包含输入输出流头文件
const int N = 1e6 + 9; // 定义一个常量N作为数组大小的上限(1e6表示10的6次方)
int a[N]; // 定义一个静态大小的全局数组a
using namespace std; // 使用std命名空间
int main() {
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0); // 提高cin和cout效率,关闭同步机制
int n, S; cin >> n >> S; // 输入数组长度n和目标和S
int ans = n + 1; // 初始化答案为n+1,这将用作无法找到满足条件子数组时的返回值
for (int i = 1; i <= n; i++) cin >> a[i]; // 循环输入n个数组元素
for (int i = 1, j = 0, sum = 0; i <= n; ++i) {
while (i > j || (j + 1 <= n && sum < S)) sum += a[++j];
if (sum >= S) ans = min(ans, j - i + 1);
sum -= a[i];
}
cout << (ans > n ? 0 : ans) << endl;
return 0;
}
在对连续子数组求和时使用了两个指针 i
和 j
。指针 i
表示当前遍历到的起始位置,指针 j
表示结束位置,并且有以下逻辑:
- 初始化指针
i
,j
都从0开始,变量sum来记录当前子段和。 - 当sum小于目标S且j没有超出右边界时,不断将a[j]加到sum并递增索引j。
- 当sum大于等于S时,计算此时连续子段长度并尝试更新答案ans。
- 减去当前左端点a[i]后再向右移动左端点(即索引i)。
最后输出结果: 如果最短长度没变则说明不存在满足条件的子段,则输出0;否则输出正确答案ans。
在时间复杂度方面,由于每个元素最多被操作2次(一次加入sum、一次从sum中减去),因此算法时间复杂度是O(n),可以处理大规模数据。
这一段代码为滑动窗口算法的实现,用于找到和至少为 S
的最短连续子数组。下面是逐步分析:
for (int i = 1, j = 0, sum = 0; i <= n; ++i) {
// 外层循环,遍历每个可能的起点i
while (i > j || (j + 1 <= n && sum < S)) {
// 如果当前的sum小于S并且子数组尾端j没有超出数组范围,
// 或者窗口大小为负(即i > j)时,则向右扩展窗口以包括a[++j]
sum += a[++j];
}
if (sum >= S) {
// 确认当前窗口[i, j]内元素和是否大于等于S
// 如果是,更新答案ans为当前窗口长度和已记录ans中的较小值
ans = min(ans, j - i + 1);
}
// 增加左端点前减去索引i指向的值,因为我们准备移动左端点至i+1,
// 当前元素将不被后续子段所包含。
sum -= a[i];
}
在执行过程中,算法尝试通过调整子数组a[i...j]
来满足条件(和至少为S),这里有两个关键步骤:
-
扩展阶段: 循环中的
while
语句负责向右扩展窗口。当sum < S
或者当前窗口大小无效 (i > j
) 时,不断地将下一个元素加入到当前求和中并递增尾指针j
。这样做增加了窗口内数值总和,并逐渐在符合条件时结束循环。 -
收缩阶段: 一旦子数组的总和达到或超过目标值
S
,算法会记录当前找到的有效子数组长度(使用min()
来确保记录最小的那个),然后从总和中减去左边界处的值以收缩窗口,并且开始尝试寻找一个更短但仍然有效的连续子数组。
由此可见,这一代码块实际上采用了“双指针”方法运行:其中一个指针代表连续子序列开始部分,在主循环中递增;另一个指针代表结束部分,在内部while循环中根据条件递增。通过合理移动两个指针来控制窗口大小,并寻找结果。