代码随想录DAY02 - 数组 - 08/01

长度最小的子数组

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

2.思路:如果没有规定是连续子数组,则可以先把数组按从大到小排序,再遍历数组求前 i 个元素的和,使其和sum ≥ s,因为子数组中元素值越大,子数组的长度就越小。但是题目要求连续子数组,也就是不可以打乱原数组的排序。既然要求子数组长度最小,那么长度可以从1开始依次递增,直到增长到数组长度。但是最后超出了时间限制。

自己写的代码,超时:

int minSubArrayLen(int target, int* nums, int numsSize) {
    int length = 1;
    int sum = 0;
    int i;
    while (sum < target){
        for (i = 0; i <= numsSize-length; ++i) {
            sum = 0;
            for (int j = 0; j < length; ++j) {
                sum += nums[i+j];
            }
            // 如果不加这个 if 会先继续往后循环
            if (sum >= target){
                break;
            }
        }
        if (length > numsSize){ // if 判断要在 length++ 之前
            return 0;
        }
        length++;
    }
    return length-1;
}

3.题解:通过滑动窗口的思路解决。在暴力解法中使用两个for循环,一个for循环遍历起始位置,另一个for循环遍历终止位置。如果要在一个for循环内完成任务,此循环的索引应是终止位置,而不是起始位置。

滑动窗口:不断调节子序列的起始位置 start 和终止位置 end。

Q1:什么时候调节窗口的起始位置?

在for的一次循环中,窗口终点固定为 end,如果窗口元素之和 sum >= target,此时以 start 为起点(且以end为终点)的最小子数组长度已被找到,则移动 start 到下一个起点,找到下一个最小数组长度。

Q2:为什么 sum >= target 放在 while循环中而不是 if ?

书接上文,当移动 start 到下一个起点时(此时终点固定为end),虽然窗口尺寸变小了,但仍有可能 sum >= target ,因此需要不断移动 start 缩小窗口尺寸,更新最小数组长度。

Q3:什么时候调节窗口的终止位置?

当 sum < target 时,窗口起点固定为start,需要扩充窗口尺寸,即让 end 继续往前滑动,使窗口内元素的和 sum >= target,此时会不断地跳过 while 循环,执行 for 循环中的 end++。

int minSubArrayLen(int target, int* nums, int numsSize) {
    int start = 0; // 窗口的起始位置,起始为 0,一定要初始化
    int end; // 窗口的终止位置
    int sum = 0; // 窗口内元素的和
    
    int subLen = 0; // 窗口大小,也是子数组长度
    int minLen = numsSize+1; // 子数组最小长度,设置比数组长度大的初始值,用于之后判断是否找到符合条件的子数组
	
    // 当 sum < target 时,需要扩充窗口尺寸,即让 end 继续往前滑动,使窗口内元素的和 sum >= target
    for (end = 0; end < numsSize; ++end) { 
        
        sum += nums[end];
        
        // 为什么这里是 while,而不是if?
        // 此时窗口终点固定为 end,如果 sum >= target,则不断移动 start 缩小窗口尺寸,找到最小数组长度
        while (sum >= target){
            subLen = end-start+1;
            if (subLen < minLen){
                minLen = subLen; // 更新子数组最小长度
            }
            // 当以 start 为起点的最小子数组长度已被找到,则移动 start 到下一个起点,找到下一个最小数组长度
            sum -= nums[start]; // 下一个窗口的元素之和要去掉上一个起点的值
            start++; // 移动起点
        }
    }
   
    // 如果遍历完没有找到满足 sum >= target 的子数组,则 minLen 仍是初始值没有改变
    if (minLen == numsSize+1){
        return 0;
    } else{
        return minLen;
    }
}

自己画的一些过程,等周末有空了再做个总结完善一下:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值