滑动窗口内数的和-LintCode

该博客介绍了如何在给定的整型数组中,使用滑动窗口方法计算每个时刻窗口内数字的和。例如,对于数组[1,2,7,8,5]和滑动窗口大小k=3,得到的和序列是[10,17,20]。" 124803688,7445999,C语言中的预处理指令#if、#ifdef、#ifndef,"['C语言', '预处理', '条件编译']

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

给你一个大小为n的整型数组和一个大小为k的滑动窗口,将滑动窗口从头移到尾,输出从开始到结束每一个时刻滑动窗口内的数的和。

样例
对于数组 [1,2,7,8,5] ,滑动窗口大小k= 3 。
1 + 2 + 7 = 10
2 + 7 + 8 = 17
7 + 8 + 5 = 20
返回 [10,17,20]

#ifndef C604_H
#define C604_H
#include<iostream>
#include<vector>
using namespace std;
class Solution {
public:
    /**
    * @param nums: a list of integers.
    * @param k: length of window.
    * @return: the sum of the element inside the window at each moving.
    */
    vector<int> winSum(vector<int> &nums, int k) {
        // write your code here
        vector<int> res;
        //若nums为空或k<=0,返回空
        if (nums.empty() || k <= 0)
            return res;
        int sum = 0;
        for (int i = 0; i < k; ++i)
            sum += nums[i];
        res.push_back(sum);
        //由于滑动窗口,sum减去nums[j-1]加上nums[j+k-1]就是现在窗口内的元素之和
        for (int j = 1; j + k - 1 < nums.size(); ++j)
        {
            sum -= nums[j - 1];
            sum += nums[j + k - 1];
            res.push_back(sum);
        }
        return res;
    }
};
#endif
<think>嗯,我现在需要解决LintCode 406题,也就是大于S的最小子组问题。题目是说给定一个由n个正整组成的一个正整s,找出该组中满足其≥s的最小长度子组,如果无解就返回-1。我得用C++来实现这个解法。首先,我需要仔细理解问题,然后思考可能的解题方法。 首先,题目要求的是子组的至少为s,并且这个子组的长度要尽可能小。子组是组中连续的元素组成的,所以不能打乱顺序。我需要找到一个连续的子组,其大于等于s,同时长度最短。 我记得之前学过的滑动窗口(双指针)方法可能适用这种情况。因为滑动窗口通常用于处理连续子组的的问题,尤其是当组元素都是正的时候。这种情况下,滑动窗口的时间复杂度可以做到O(n),这应该是比较高效的。 让我再仔细想想滑动窗口的思路。滑动窗口通常维护一个窗口,用左右两个指针,右指针不断向右移动,直到窗口内的满足条件,然后尝试移动左指针尽可能缩小窗口,同时记录最小的窗口长度。这样,每次右指针移动后,左指针可能移动多次,直到窗口再次小于s,然后继续移动右指针。整个过程只需要遍历组两次,所以时间复杂度是O(n)。 比如,假设组是[2,3,1,2,4,3],s=7。初始时左右指针都在0位置,sum=0。然后右指针不断移动,sum增加,直到sum≥7。比如当右指针移动到3的位置(元素是2,此时sum=2+3+1+2=8),这时候满足条件。然后左指针开始移动,看看能不能缩小窗口。左指针移动到1,sum变为3+1+2=6,这时候不满足,于是右指针继续移动。当右指针移动到4的位置(元素4),sum=3+1+2+4=10≥7。左指针再移动,此时左指针在1的位置,右指针在4,窗口长度是4(元素3,1,2,4),长度是4。然后继续移动左指针到2,sum=1+2+4=7,满足条件,此时窗口长度是3。接着继续移动左指针到3,sum=2+4=6,不满足,所以右指针继续移动到5(元素3),sum=2+4+3=9≥7。左指针移动到4,sum=4+3=7,此时窗口长度是2。所以最终最小的长度是2。 这个例子中,滑动窗口方法确实有效。那为什么滑动窗口适用于这个问题呢?因为组中的元素都是正整,当窗口扩大时,sum增加,缩小窗口时sum减少。因此,一旦窗口内的sum≥s,我们可以安全地移动左指针,缩小窗口,而不会错过更小的可能解。如果组中有负,那么这种方法可能不适用,因为当窗口缩小时sum可能反而增加,导致无法正确判断。 所以解题步骤大概是这样的: 初始化左指针left=0,右指针right=0,当前窗口的sum=0,最小长度min_len设为无穷大。然后循环遍历组: 1. 将右指针的元素加到sum中,然后右指针右移。 2. 当sum≥s时,进入内层循环,尝试移动左指针缩小窗口: a. 计算当前窗口长度right - left,更新min_len。 b. 减去左指针指向的元素,左指针右移。 c. 重复直到sum < s。 3. 循环结束后,如果min_len还是无穷大,说明没有符合条件的子组,返回-1,否则返回min_len。 那这样的算法时间复杂度是O(n),每个元素最多被左右指针各访问一次。 接下来,我需要考虑边界情况,比如当组中有一个元素正好等于s的情况,此时应该返回1。或者整个组的刚好等于s,此时返回组长度。或者所有元素的都不足s,返回-1。 比如测试样例中的输入样例,可能需要测试不同的情况。例如,题目中的样例,输入组为[2,3,1,2,4,3],s=7,输出应该是2,对应子组[4,3]。 接下来,我需要用C++实现这个滑动窗口的方法。步骤大致如下: - 初始化left=0,sum=0,min_len为一个很大的值,比如INT_MAX。 - 遍历right从0到n-1: sum += nums[right] while (sum >= s) { min_len = min(min_len, right - left +1) sum -= nums[left] left++ } - 最后,如果min_len还是INT_MAX,返回-1,否则返回min_len。 这样应该可以解决问题。例如,当sum >=s时,不断缩小窗口,并记录每次可能的长度,取最小值。 但需要确保组元素都是正整,否则可能无法正确工作。题目中说明组是正整,所以没问题。 那现在编写代码的时候需要注意一些细节,比如组为空的情况,或者当组中没有满足条件的子组时返回-1。 另外,需要测试一些情况: 测试用例1: 输入:nums = [2,3,1,2,4,3], s=7 预期输出:2,因为子组[4,3]的是7,长度2。 测试用例2: 输入:nums = [1,4,4], s=4 预期输出:1,因为元素4单独存在。 测试用例3: 输入:nums = [1,2,3], s=10 预期输出:-1,总是6不够。 现在,编写C++代码: 根据上述思路,大致代码结构是: int minimumSize(vector<int> &nums, int s) { int n = nums.size(); if(n ==0) return -1; int left =0; int sum=0; int min_len = INT_MAX; for(int right=0; right <n; right++){ sum += nums[right]; while(sum >=s){ min_len = min(min_len, right - left +1); sum -= nums[left]; left++; } } return (min_len == INT_MAX)? -1 : min_len; } 但是,这可能需要进一步检查是否正确。例如,当组中有多个元素满足时,是否正确取到最小长度? 比如测试用例1中的过程: right=0,sum=2,不满足s=7。 right=1,sum=5,仍不满足。 right=2,sum=6,不满足。 right=3,sum=8,此时进入循环: min_len=4-0+1=4(窗口是0~3,长度4)。 sum-=nums[0]=2,sum=6,left=1。 此时sum=6<7,退出循环。 继续right=4,sum=6+4=10≥7: 进入循环,计算长度4-1+1=4?不,此时right是4,left是1。长度是4-1+1=4。 然后sum-=nums[1]=3,sum=7,left=2。再次循环,此时sum=7≥7: 计算长度4-2+1=3,min_len更新为3。 sum-=nums[2]=1,sum=6,left=3。退出循环。 继续right=5,sum=6+3=9≥7: 进入循环,计算长度5-3+1=3。此时min_len是之前记录的3现在的3,保持不变。 sum-=nums[3]=2,sum=7,left=4。再次循环,sum=7≥7: 计算长度5-4+1=2,此时min_len更新为2。 sum-=nums[4]=4,sum=3,left=5。退出循环。 循环结束,返回2。正确。 所以,这个算法应该能正确计算。 但是,有没有可能漏掉某些情况?比如,当窗口内的刚好等于s时,是否可能更早地出现更小的窗口?比如在移动左指针的过程中,可能找到更小的窗口。例如,假设有一个窗口的超过s,但之后左指针移动后,sum减少,但可能还存在更小的窗口。 比如,假设组是[1,2,3,4,5],s=9。总1+2+3+4=10≥9,窗口长度4。然后sum减去1,得到9,此时窗口是2+3+4=9,长度3,这时会被计算到。之后sum减2得到7,退出循环。此时右指针继续移动,sum加5得到12,进入循环,计算长度4(窗口是3+4+5=12)。然后sum减3得到9,长度3。所以最终最小长度是3。而实际存在更小的窗口,比如4+5=9,长度2。这说明我的算法有问题吗? 哦,这似乎有问题。因为在上述情况下,当右指针到5的位置时,sum是3+4+5=12。此时进入循环,计算窗口长度3(3,4,5),此时min_len更新为3。然后sum减去3得到9,此时sum=9≥s=9。于是再次进入循环,此时窗口是4+5=9,长度是2。这时候min_len会被更新为2。所以算法是正确的吗? 是的,这个时候会进入循环两次: 第一次sum=12≥9,计算right=4(索引从0开始?假设组是0-based,原组是[1,2,3,4,5],索引0~4。当右指针到4时,sum=1+2+3+4+5=15?或者原题中的例子可能不同? 或者原问题中的组是正整,而上面这个例子中,假设s=9。初始时,假设组是[1,2,3,4,5],s=9。那正确的解应该是子组[4,5],长度2。 现在用算法模拟: 初始化left=0,sum=0,min_len=INT_MAX. right=0: sum=1. sum <9 →不处理。 right=1: sum=3. 仍不。 right=2: sum=6. 仍不。 right=3: sum=10≥9. 进入循环: 计算窗口长度3-0+1=4。min_len=4. sum -=1 →sum=9. left=1. 再次检查sum >=9?是的,再次循环: 计算窗口长度3-1+1=3 →min_len=3. sum -=2 →sum=7. left=2. sum <9 →退出。 继续right=4: sum +=5 →7+5=12≥9. 进入循环: 窗口长度4-2+1=3 →min_len现在是3之前的3,一样。 sum -=3 →sum=9. left=3. 再次检查sum >=9 →是的。 窗口长度4-3+1=2 →min_len更新为2. sum -=4 →sum=5. left=4. 退出循环。 循环结束。返回min_len=2,正确。 这说明算法在这种情况下也能正确计算。因此,该算法是正确的。 那这说明滑动窗口方法是可行的,且能正确找到最小长度的子组。 所以,现在我需要将这个思路转化为C++代码,并且处理可能的边界条件。 另一个测试案例:当组中有多个元素等于s的情况。例如,nums=[5,1,3,5], s=5。这时最小长度是1,因为有一个元素5。当滑动窗口遍历到第一个元素时,sum=5≥5,窗口长度1,此时min_len=1。然后sum减5,left=1。继续处理其他元素,但结果已经是1,所以正确。 因此,算法是正确的。 综上,解题思路是使用滑动窗口法,时间复杂度O(n),空间复杂度O(1)。 现在,我需要根据这个思路写出C++代码,并确保正确。</think>### LintCode 406. 大于S的最小子组 解题思路 #### 问题分析 题目要求在给定**正整组**中找到**≥s**的**最短连续子组**的长度。若不存在则返回-1。由于组元素均为正,可以使用**滑动窗口(双指针)**方法高效解决,时间复杂度为$O(n)$。 #### 算法思路 1. **初始化**:左指针`left=0`,当前窗口`sum=0`,最小长度`min_len`设为极大值(如`INT_MAX`)。 2. **滑动窗口**: - 右指针`right`遍历组,将元素累加到`sum`。 - 当`sum≥s`时,尝试缩小窗口(左指针右移)以找到更短的有效子组: - 更新`min_len`为当前窗口长度`right-left+1`。 - 减去左指针元素并右移左指针,直到`sum<s`。 3. **结果判断**:若`min_len`未被更新,返回-1,否则返回`min_len`。 #### 代码实现 ```cpp #include <vector> #include <climits> // 用于INT_MAX using namespace std; int minimumSize(vector<int> &nums, int s) { int n = nums.size(); if (n == 0) return -1; int left = 0; int sum = 0; int min_len = INT_MAX; for (int right = 0; right < n; right++) { sum += nums[right]; // 当sum >= s时,尝试缩小窗口 while (sum >= s) { min_len = min(min_len, right - left + 1); sum -= nums[left]; left++; } } return (min_len == INT_MAX) ? -1 : min_len; } ``` #### 复杂度分析 - **时间复杂度**:$O(n)$,每个元素最多被左右指针各访问一次。 - **空间复杂度**:$O(1)$,仅需常级额外空间。 #### 示例验证 - **输入**:`nums = [2,3,1,2,4,3]`, `s = 7` - **过程**:窗口`[4,3]`为7,长度2。 - **输出**:2。 - **输入**:`nums = [1,4,4]`, `s = 4` - **输出**:1(直接取单个元素4)。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值