3月2日 快慢指针之 最小连续子数组和(美丽的区间)

这段代码是一个 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 表示结束位置,并且有以下逻辑:

  • 初始化指针 ij都从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),这里有两个关键步骤:

  1. 扩展阶段: 循环中的 while 语句负责向右扩展窗口。当 sum < S 或者当前窗口大小无效 (i > j) 时,不断地将下一个元素加入到当前求和中并递增尾指针 j。这样做增加了窗口内数值总和,并逐渐在符合条件时结束循环。

  2. 收缩阶段: 一旦子数组的总和达到或超过目标值 S,算法会记录当前找到的有效子数组长度(使用 min() 来确保记录最小的那个),然后从总和中减去左边界处的值以收缩窗口,并且开始尝试寻找一个更短但仍然有效的连续子数组。

由此可见,这一代码块实际上采用了“双指针”方法运行:其中一个指针代表连续子序列开始部分,在主循环中递增;另一个指针代表结束部分,在内部while循环中根据条件递增。通过合理移动两个指针来控制窗口大小,并寻找结果。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值