解题思路:LeetCode 第 209 题 “Minimum Size Subarray Sum”
在这篇博文中,我们将探讨如何使用 Swift 解决 LeetCode 第 209 题 “Minimum Size Subarray Sum”。我们会讨论两种方法:暴力法和滑动窗口法,并对这两种方法的时间复杂度和空间复杂度进行分析。
问题描述
给定一个包含正整数的数组和一个正整数 target
,找到该数组中满足其和大于等于 target
的最小长度子数组,并返回其长度。如果不存在这样的子数组,则返回 0。
方法一:暴力法
暴力法的基本思路是枚举数组中所有可能的子数组,计算每个子数组的和,并记录符合条件的最小长度。
实现步骤
- 初始化一个变量
minLength
为无穷大,用于存储满足条件的最小子数组长度。 - 使用两层循环,外层循环确定子数组的起点,内层循环确定子数组的终点,并计算子数组的和。
- 如果找到的子数组和大于等于
target
,更新minLength
。 - 返回
minLength
,如果minLength
仍然是无穷大,说明没有符合条件的子数组,返回 0。
func minSubArrayLenBruteForce(_ target: Int, _ nums: [Int]) -> Int {
let n = nums.count
var minLength = Int.max
for i in 0..<n {
var sum = 0
for j in i..<n {
sum += nums[j]
if sum >= target {
minLength = min(minLength, j - i + 1)
break
}
}
}
return minLength == Int.max ? 0 : minLength
}
时间复杂度和空间复杂度分析
- 时间复杂度: (O(n^2))。由于需要枚举所有可能的子数组,因此有两个嵌套的循环,时间复杂度为平方级别。
- 空间复杂度: (O(1))。仅使用了常数级别的额外空间。
方法二:滑动窗口法
滑动窗口法通过两个指针动态维护一个窗口,用来表示当前子数组。当窗口的和大于等于 target
时,尝试缩小窗口的大小,以找到更短的子数组。
实现步骤
- 初始化两个指针
left
和right
以及一个变量sum
用来保存当前窗口内的元素和。 - 使用
right
指针遍历数组,逐步增加窗口的大小。 - 每次增加
right
指针后,更新sum
,并检查是否满足sum >= target
。 - 如果满足条件,记录当前窗口的长度,并尝试通过增加
left
指针来缩小窗口,从而找到更短的满足条件的子数组。 - 最后,返回找到的最小长度,如果没有找到满足条件的子数组,则返回 0。
func minSubArrayLen(_ target: Int, _ nums: [Int]) -> Int {
var left = 0
var sum = 0
var minLength = Int.max
for right in 0..<nums.count {
sum += nums[right]
while sum >= target {
minLength = min(minLength, right - left + 1)
sum -= nums[left]
left += 1
}
}
return minLength == Int.max ? 0 : minLength
}
时间复杂度和空间复杂度分析
- 时间复杂度: (O(n))。
right
和left
指针各遍历数组一次,时间复杂度为线性级别。 - 空间复杂度: (O(1))。仅使用了常数级别的额外空间。
结论
通过对比可以看出,滑动窗口法相比暴力法在时间复杂度上有显著优势,尤其是在处理大规模数据时,滑动窗口法的线性时间复杂度使其更加高效。建议在实际开发中优先使用滑动窗口法来解决类似问题。