目录
🌟 LeetCode 1658: 将 x 减到 0 的最小操作数(滑动窗口经典题)
🌟 LeetCode 1658: 将 x 减到 0 的最小操作数(滑动窗口经典题)
📘 题目描述
给你一个整数数组 nums
和一个整数 x
。你每次可以选择 移除数组最左边或最右边的一个元素,然后从 x
中减去该元素的值。注意,每次操作后 nums
会发生改变。
请你返回将 x
减到 0 所需的最小操作数,如果无法做到,则返回 -1
。
🔍 示例
输入:nums = [1, 1, 4, 2, 3], x = 5
输出:2
解释:移除右边的 3 和 2,x 变为 0。共用了 2 次操作。
输入:nums = [5, 6, 7, 8, 9], x = 4
输出:-1
解释:无法移除若干元素使得和为 4
💡 解题分析
我们最直接的想法是:枚举所有可能的左右组合,看看移除哪些元素能恰好让 x
变成 0。但这显然效率很低(时间复杂度 O(n²) 甚至更高)。
所以我们要换一种思路——转化问题模型。
🧠 转化思路:从减法转成保留一段连续子数组
将整个数组和记为 total = sum(nums)
。假设我们移除了若干前缀和后缀元素,它们的总和为 x
。那我们保留下来的中间部分的和就是 total - x
。
于是,原问题就变成了:
找出一个连续子数组,其和为 total - x
,且长度最长。
因为我们只允许移除数组的两端,所以保留的中间部分必须连续,这就是典型的滑动窗口应用场景。
🧪 解题方法:滑动窗口寻找最长连续子数组
✅ 算法步骤
- 计算总和
total
- 目标转化为找出一段连续子数组,其和为
target = total - x
- 使用滑动窗口从左到右遍历,维护当前窗口的和
curr_sum
- 当
curr_sum > target
,窗口左端右移,直到和不大于target
- 如果
curr_sum == target
,更新当前最长子数组长度 - 遍历结束后,若存在满足条件的子数组,返回
len(nums) - max_len
;否则返回-1
🧾 Python 实现
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
total = sum(nums)
target = total - x
if target < 0:
return -1
if target == 0:
return len(nums)
max_len = -1
left = 0
curr_sum = 0
for right in range(len(nums)):
curr_sum += nums[right]
# 窗口收缩,直到 curr_sum <= target
while curr_sum > target and left <= right:
curr_sum -= nums[left]
left += 1
if curr_sum == target:
max_len = max(max_len, right - left + 1)
return len(nums) - max_len if max_len != -1 else -1
📈 时间与空间复杂度分析
类型 | 复杂度 |
时间复杂度 | O(n) |
空间复杂度 | O(1) |
- 时间复杂度为
O(n)
:每个元素最多被访问两次(一次右扩、一次左缩) - 空间复杂度为
O(1)
:只使用了几个变量存储状态,未使用额外结构
🔁 示例详解
示例 1
nums = [1, 1, 4, 2, 3], x = 5
total = 11
target = total - x = 6
我们希望找出一个连续子数组,和为 6
满足条件的子数组是 [4, 2](下标 2 ~ 3),长度为 2
所以,最少需要移除的次数为 len(nums) - 2 = 3
但题目希望保留的是最长的子数组,其它部分都移除,所以操作数是:5 - 2 = 3
示例 2
nums = [5, 6, 7, 8, 9], x = 4
total = 35, target = 31
没有任何连续子数组的和为 31,返回 -1
🔄 其他解法对比
1. 暴力解法(超时)
枚举所有左端和右端的组合,尝试求和为 x
。但这种方法时间复杂度为 O(n²),无法应对大型数据。
2. 前缀和 + 哈希优化(复杂一些)
我们可以尝试记录前缀和和对应的索引,然后倒过来处理右端,试图找到满足 x - right_sum = left_sum
的组合。但实现复杂,不如滑动窗口直观。
✅ 总结
这道题巧妙地将“从两端移除求和”的问题,转化为了“保留一段连续子数组”的问题,从而可以用经典的滑动窗口技巧在线性时间内解决,值得深入理解。
📌 技术要点
- 问题转化为滑动窗口模型
- 理解前缀 + 后缀移除 = 中间保留
- 使用滑动窗口寻找最长连续子数组满足目标和