长度最小的子数组-209

题目

力扣icon-default.png?t=M1L8https://leetcode-cn.com/problems/minimum-size-subarray-sum/

题解

力扣icon-default.png?t=M1L8https://leetcode-cn.com/problems/minimum-size-subarray-sum/solution/chang-du-zui-xiao-de-zi-shu-zu-by-leetcode-solutio/

方法一:自己写的解

 求和的地方不够优,见方法三大牛的写法

package algorithm.arrayListTest;


public class Solution_8 {

    /**
     * 输入:target = 7, nums = [2,3,1,2,4,3]
     * 输出:2
     * 解释:子数组 [4,3] 是该条件下的长度最小的子数组
     */
    public static void main(String[] args) {
        System.out.println(new Solution_8().minSubArrayLen(7, new int[]{2, 3, 1, 2, 4, 3}));
    }


    public int minSubArrayLen(int target, int[] nums) {
        int left = 0, right = 0, n = nums.length;
        int ans = Integer.MAX_VALUE;
        while (left <= right && right < n) {
            int sumOfWindow = sumSubArrays(nums, left, right);
            if (sumOfWindow >= target) {
                ans = Math.min(ans, right - left + 1);
                left++;
            } else {
                right++;
            }
        }
        if (ans == Integer.MAX_VALUE) {
            return 0;
        }
        return ans;

    }

    private int sumSubArrays(int[] nums, int left, int right) {
        if (left > right) {
            return 0;
        }

        if (left == right) {
            return nums[left];
        }

        int sum = 0;
        for (int i = left; i <= right && i < nums.length; i++) {
            sum += nums[i];
        }
        return sum;
    }
}

方法二:前缀和 + 二分查找

  • 因为这道题保证了数组中每个元素都为正,所以前缀和一定是递增的,这一点保证了二分的正确性
  • 需要理解二分查找中,返回负数表示什么,我刚开始也没想明白
  • 先把数据处理一遍,将前i个元素累加和存到另外一个数组,这个好像和动态规划有点像,求满足条件的最小子序列长度,还没实现这个版本,等练习了动规再来看

   public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }

        int ans = Integer.MAX_VALUE;

        /**
         * 为了方便计算,令 size = n + 1
         * 其中 sums[i]       表示从 nums[0]到 nums[i-1]的元素和
         * sums[0] = 0       表示前 0 个元素的前缀和为 0
         * sums[1] = nums[0] 表示前 1 个元素的前缀和为 nums[0]
         * 以此类推
         */
        int[] sums = new int[n + 1];
        for (int i = 1; i <= n; i++) {
            //nums[i - 1] 表示第i个元素,因为从下标0开始计算
            sums[i] = sums[i - 1] + nums[i - 1];
        }


        for (int i = 1; i <= n; i++) {
            int searchValue = target + sums[i - 1];

            //此处的 sums[bound] 表示前 bound个值的和,bound为下标
            //sums[i - 1] + target <= sums[bound]

            int bound = binarySearch(sums, 0, sums.length, searchValue);

            //表示没找到
            if (bound < 0) {
                bound = -bound - 1;
                //这里的bound也是答案的一种
            }

            if (bound <= n) {
                ans = Math.min(ans, bound - (i - 1));
            }
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }

    /**
     * 到了就返回目标值得下标位置
     * 找不到就返回-(low + 1),负数是为了返回没找到,但是第一个大于目标值值得位置信息,需要将返回值取反-1就得到第一个大于目标值得位置
     */
    private Integer binarySearch(int[] a, int fromIndex, int toIndex,
                                 int key) {
        int low = fromIndex;
        int high = toIndex - 1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            int midVal = a[mid];

            if (midVal < key)
                low = mid + 1;
            else if (midVal > key)
                high = mid - 1;
            else
                return mid; // key found
        }

        //-(第一个大于目标值得下标位置 + 1)
        return -(low + 1);  // key not found.
    }

方法三:滑动窗口

  • 滑动窗口内的值可以动态的计算,我自己写的方法一中的是每次都计算,复杂了
  • 滑动窗口的常规需要记住的一些套路
    • 窗口缩小:左指针移动一个
    • 窗口增加:右指针移动一个
    • 窗口整体移动,且窗口大小不变:左右指针都移动一个位置
    • 求滑动窗口的累加和
    • 求滑动窗口的窗口两端的差值
    • 滑动窗口测速
   class Solution {
        public int minSubArrayLen(int target, int[] nums) {
        int n = nums.length;
        if (n == 0) {
            return 0;
        }
        int ans = Integer.MAX_VALUE;
        int start = 0, end = 0;
        int sum = 0;
        
        while (end < n) {
            //如果窗口向右边增加一个元素,窗口之内的元素累加和也要增加一个
            sum += nums[end];

            while (sum >= target) {
                ans = Math.min(ans, end - start + 1);

                //如果窗口左边缩小一个,窗口之内的元素累加和也要减去缩减的那个元素
                sum -= nums[start];
                start++;
            }
            end++;
        }
        return ans == Integer.MAX_VALUE ? 0 : ans;
    }
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值