数组-209长度最小的子数组-中等-20210906

本文探讨如何通过暴力解法、双指针技巧和前缀和+二分查找优化算法,解决数组中和至少达到目标的最小子数组长度问题。重点讲解了三种方法的时间复杂度及其实现,并特别介绍了如何将暴力解法的时间复杂度降低到O(n log n)。
摘要由CSDN通过智能技术生成

数组-209长度最小的子数组-中等-20210906

1. 题目描述

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

找出该数组中满足其和≥ target的长度最小的 连续子数组 [numsl, numsl+1, ..., numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。

示例

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

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

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

提示

  • 1 <= target <= 10^9
  • 1 <= nums.length <= 10^5
  • 1 <= nums[i] <= 10^5

2. 题目解答

2.1 暴力解法

思路:两个for循环

第二个for循环,不断加和,直到大于目标值;此时返回子序列长度j-i+1,和之前的值比较并保留小的

时间复杂度: O ( n 2 ) O(n^2) O(n2)

空间复杂度: O ( 1 ) O(1) O(1)

class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if not nums: return 0  # 先判断数组是否为空
        n = len(nums)
        sub = n + 1  # 定义一个最大值
        for i in range(n):
            total = 0
            for j in range(i, n):
                total += nums[j]
                if total >= target:
                    sub = min(sub, j - i + 1)
                    break
        return 0 if sub == n + 1 else sub
    	#return sub if sub < n + 1 else 0

2.2 双指针/滑动窗口

根据当前子序列和大小的情况,不断的调节子序列的起始位置和终止位置,从而得出要想的结果

时间复杂度:O(n)
每个元素在滑动窗后进来操作一次,出去操作一次,每个元素都是被被操作两次,所以时间复杂度是2 * n 【其中 n 是数组的长度。指针 start 和 end 最多各移动 n次。】

空间复杂度:O(1)

  • 窗口:满足其和 ≥ s 的长度最小的 连续 子数组
  • 窗口的起始位置:当前窗口的值大于s,窗口向前移动
  • 窗口的结束位置:遍历数组的指针
class Solution:
    def minSubArrayLen(self, target: int, nums: List[int]) -> int:
        if not nums: return 0
        n = len(nums)
        sub = n + 1  # 定义一个最大值  无限大数float("inf")
        i, total = 0, 0  # i滑动窗口起始位置
        for j in range(n):
            total += nums[j]
            while total >= target:
                sub = min(sub, j - i + 1)
                total -= nums[i]
                i += 1
        return 0 if sub == n + 1 else sub

2.3 前缀和+二分查找

进阶:

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

因为这道题保证了数组中每个元素都为正,所以前缀和一定是递增的,这一点保证了二分的正确性。如果题目没有说明数组中每个元素都为正,这里就不能使用二分来查找这个位置了。

思路:方法一的时间复杂度是 O ( n 2 ) O(n^2) O(n2),因为在确定每个子数组的开始下标后,找到长度最小的子数组需要$ O(n) 的 时 间 。 如 果 使 用 二 分 查 找 , 则 可 以 将 时 间 优 化 到 的时间。如果使用二分查找,则可以将时间优化到 使O(logn)$。

  • 额外创建一个数组$ \text{sums} 用 于 存 储 数 组 用于存储数组 \text{nums}$ 的前缀和,其中 sums [ i ] \text{sums}[i] sums[i]表示从$ \text{nums}[0] 到 到 \text{nums}[i-1] $的元素和
  • 得到前缀和之后,对于每个开始下标$ i$,可通过二分查找得到大于或等于 i i i 的最小下标$ \textit{bound}$,使得 sums [ bound ] − sums [ i − 1 ] ≥ s \text{sums}[\textit{bound}]-\text{sums}[i-1] \ge s sums[bound]sums[i1]s
  • 更新子数组的最小长度(此时子数组的长度是$ \textit{bound}-(i-1)$)。
class Solution:
    def minSubArrayLen(self, s: int, nums: List[int]) -> int:
        if not nums:
            return 0
        
        n = len(nums)
        ans = n + 1
        sums = [0]
        for i in range(n):
            sums.append(sums[-1] + nums[i])
        
        for i in range(1, n + 1):
            target = s + sums[i - 1]  # 加上前面所有和
            bound = bisect.bisect_left(sums, target)
            if bound != len(sums):
                ans = min(ans, bound - (i - 1))
        
        return 0 if ans == n + 1 else ans

时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn)

空间复杂度: O ( n ) O(n) O(n)

3. 错解


用kotlin解决这个题目:2653. 滑动数组的美丽值 提示 中等 26 相关企业 给你一个长度为 n 的整数数组 nums ,请你求出每个长度为 k 的数组的 美丽值 。 一个数组的 美丽值 定义为:如果数组中第 x 小整数 是 负数 ,那么美丽值为第 x 小的数,否则美丽值为 0 。 请你返回一个包含 n - k + 1 个整数的数组,依次 表示数组中从第一个下标开始,每个长度为 k 的数组的 美丽值 。 数组指的是数组中一段连续 非空 的元素序列。 示例 1: 输入:nums = [1,-1,-3,-2,3], k = 3, x = 2 输出:[-1,-2,-2] 解释:总共有 3 个 k = 3 的数组。 第一个数组是 [1, -1, -3] ,第二小的数是负数 -1 。 第二个数组是 [-1, -3, -2] ,第二小的数是负数 -2 。 第三个数组是 [-3, -2, 3] ,第二小的数是负数 -2 。 示例 2: 输入:nums = [-1,-2,-3,-4,-5], k = 2, x = 2 输出:[-1,-2,-3,-4] 解释:总共有 4 个 k = 2 的数组。 [-1, -2] 中第二小的数是负数 -1 。 [-2, -3] 中第二小的数是负数 -2 。 [-3, -4] 中第二小的数是负数 -3 。 [-4, -5] 中第二小的数是负数 -4 。 示例 3: 输入:nums = [-3,1,2,-3,0,-3], k = 2, x = 1 输出:[-3,0,-3,-3,-3] 解释:总共有 5 个 k = 2 的数组。 [-3, 1] 中最小的数是负数 -3 。 [1, 2] 中最小的数不是负数,所以美丽值为 0 。 [2, -3] 中最小的数是负数 -3 。 [-3, 0] 中最小的数是负数 -3 。 [0, -3] 中最小的数是负数 -3 。 提示: n == nums.length 1 <= n <= 105 1 <= k <= n 1 <= x <= k -50 <= nums[i] <= 50
05-24
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值