九章算法 | Facebook 面试题:和大于S的最小子数组

给定一个由 ​n​ 个正整数组成的数组和一个正整数 ​s​ ,请找出该数组中满足其和 ≥ s 的最小长度子数组。如果无解,则返回 -1。

在线评测地址:LintCode 领扣

样例 1:

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

样例 2:

输入: [1, 2, 3, 4, 5], s = 100
输出: -1 

解题思路

  • 如果采用暴力解法,用变量​i​从左到右遍历数组,变量​j​从​i​到数组尾部遍历,将​i​到​j​内的元素遍历求和,找到和大于​s​的最短数组。时间复杂度为O(n^3)。
  • 对暴力法进行优化,使用累加器来进行求和,那么求和这步只需要O(1)。总的时间复杂度为O(n^2)。
  • 使用二分法来继续优化,对于左端点​i​,我们用二分法来寻找​j​。首先建立前缀和数组​sum​,对于每个​i​,在​i​到尾部这段区间上二分查找,找到满足​sum[j] - sum[i] > S​的最小的​j​。总的时间复杂度为O(nlog(n))。
  • 最优解法是采用滑窗。我们用 2 个指针,一个指向子数组开始的位置,一个指向数子组最后的位置,并维护区间内的和 ​curr_sum​大于等于​s​ 同时数组长度最小,实时更新最短的数组长度​res​。时间复杂度为O(n)。

算法流程

  • 初始化左指针​left​和右指针​right​指向​0​,子数组和​curr_sum​为0。
  • 右指针​right​遍历​nums​数组,即不断移动滑窗右端
    • 更新子数组的和,​curr_sum += nums[right]​
    • 当子数组和满足条件,即​curr_cum >= s​时
      • 更新​res = min(res, right - left + 1)​,其中​right - left + 1​是当前子数组的长度
      • ​curr_sum -= nums[left]​,然后左指针右移,继续判断当前数组和是否满足条件
  • 返回​res​

复杂度分析

  • 时间复杂度:O(n) 。每个指针移动都需要O(n)的时间。每个元素至多被访问两次,一次被右端点访问,一次被左端点访问。
  • 空间复杂度:O(1)。变量只需要常数空间。
public class Solution {
    /**
     * @param nums: an array of integers
     * @param s: An integer
     * @return: an integer representing the minimum size of subarray
     */
    public int minimumSize(int[] nums, int s) {
        int left = 0, currSum = 0, res = Integer.MAX_VALUE;
        for (int right = 0; right < nums.length; right ++) {
            // 滑窗右边界扩张
            currSum += nums[right];
            // 满足条件
            while (currSum >= s) {
                // 更新res
                res = Math.min(res, right - left + 1);
                // 滑窗左边界收缩
                currSum -= nums[left];
                left ++;
            }
        }
        return res == Integer.MAX_VALUE ? -1: res;
    }
}

更多题解参考:九章算法

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值