【面试】运算器-⑦长度最小的子数组

209. 长度最小的子数组

感谢力扣!

给定一个含有 n 个正整数的数组和一个正整数 target 。

找出该数组中满足其总和大于等于 target 的长度最小的 连续

子数组

 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度如果不存在符合条件的子数组,返回 0 。

示例 1:

输入:target = 7, nums = [2,3,1,2,4,3]
输出:2
解释:子数组 [4,3] 是该条件下的长度最小的子数组。

示例 2:

输入:target = 4, nums = [1,4,4]
输出:1

示例 3:

输入:target = 11, nums = [1,1,1,1,1,1,1,1]
输出:0

提示:

  • 1 <= target <= 109
  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 105

进阶:

  • 如果你已经实现 O(n) 时间复杂度的解法, 请尝试设计一个 O(n log(n)) 时间复杂度的解法。

1.分析题目

        输入是一个正整数和一个正整数数组,找出该数组中长度最小的,总和大于等于target的连续子数组, 返回其长度。感觉这个也可以用前缀和解诶,思路的话因为是要大于等于target,又全部都是正数,所以n(假设找到的数组的数据个数)从大到小遍历,所以如果长的n的最大值小于target就不需要继续找了,下面肯定找不到。然后找的时候是可以用三层循环来做,也可以用前缀和来做,思路清晰的话,计划如下:

        ① 编写代码-三重循环并测试验证;

        ② 编写代码-前缀和并测试验证;

        ③ 看题解;

        ④ 测试验证;

        ⑤ 总结。

2.三重循环

        思路就是遍历求子数组处理,但是n^3的时间复杂度,虽然做了一下过滤,但是理论上还是有这么大。

    public int minSubArrayLen(int target, int[] nums) {
        int result = 0;
        for (int n = nums.length-1; n >=0; n--) {
            int max = -1;
            for(int i=0;i+n<nums.length;i++){
                int m=i+n;
                int sum=0;
                for (int j = i; j <= m; j++) {
                    sum += nums[j];
                }
                max = Math.max(sum, max);
                if (sum >= target) {
                    result = n+1;
                }
            }
            if (max < target) {
                break;
            }
        }
        return result;
    }

显而易见的还是没有过

没过也不优化了,直接前缀和试一下;

3.前缀和解

        和昨天的思路一样,先求出前缀和,然后用双重循环遍历得到解。

    public int minSubArrayLen(int target, int[] nums) {
        int result = nums.length + 1;
        int[] preSum = new int[nums.length];
        int sum = 0;

        for (int i = 0; i < nums.length; i++) {
            sum += nums[i];
            preSum[i] = sum;
            if(sum>=target && (i+1)<result){
                result = i+1;
            }
        }

        for (int i = 0; i < preSum.length; i++) {
            for (int j = i + 1; j < preSum.length; j++) {
                int temp = preSum[j] - preSum[i];
                if(temp >=target && (j-i)<result){
                    result = j - i;
                }
            }
        }
        result = result == nums.length + 1 ? 0 : result;
        return result;
    }

然后,竟然还是超时,我的天!

4.学习题解

        题解用的暴力法比我少一次循环,我思维太死板了,硬是把两次循环可以搞定的事情给搞成了三次循环,其实双重循环遍历过去所有的和也都求到了;然后前缀和呢也只写了一半,还需要配合二分查找,JAVA的话是Arrays.binarySearch,可以返回大于等于某个数的第一个位置;最后是滑动窗口,要用start和end两个变量存下标,然后通过start的有条件移动+end的有条件移动来得到题解,在当前这道题目上:

        对start~end的所有元素求和sum

       end移动条件:当前sum<target时移动end

        start移动条件:当前sum>=target时,保存当前长度end-start+1,移动start

根据思路提示编写代码如下所示:

    public int minSubArrayLen(int target, int[] nums) {
        int start = 0, end = 0, sum = 0;
        int result = nums.length+1;
        while(end<nums.length){
            sum += nums[end];
            while (sum >= target) {
                result = Math.min(end - start + 1,result);
                sum -= nums[start];
                start++;
            }
            end++;
        }
        result = result == nums.length + 1 ? 0 : result;
        return result;
    }

        写的过程中有个点没有注意到, result要求最小的,忘记加Math.min了,导致求出来的结果不对。

5.简单总结

        今天这个题目花的时间比较多,用了几种方法尝试发现自己考虑得都不够周全,很多代码的细节没有注意到,吭哧吭哧就在那里写,感觉接下来的时候还是要沉着一点,思考的过程中多问一下没有别的办法了嘛?但是也要考虑时间周期,没有什么事情是尽善尽美的,同时如果前人已经有答案了,要虚心学习和思考,内化成自己的东西,这么有什么丢脸的,并没有证明自己很笨,人类就是这么发展起来的,更别说个人了,加油!

        ① 算法不是只能用一个的,要明白算法优化的核心问题是什么,然后在解题的过程中去根据需求选算法;

        ② 一般题目可以思考的边界条件:算法全都不满足怎么办?所有判断全都是最坏情况怎么办?负数怎么办?下标操作有可能越界吗?常总结,常考虑。

       

  • 26
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值