1658. 将 x 减到 0 的最小操作数

目录

🌟 LeetCode 1658: 将 x 减到 0 的最小操作数(滑动窗口经典题)

📘 题目描述

🔍 示例

💡 解题分析

🧠 转化思路:从减法转成保留一段连续子数组

🧪 解题方法:滑动窗口寻找最长连续子数组

✅ 算法步骤

🧾 Python 实现

📈 时间与空间复杂度分析

🔁 示例详解

示例 1

示例 2

🔄 其他解法对比

1. 暴力解法(超时)

2. 前缀和 + 哈希优化(复杂一些)

✅ 总结

📌 技术要点


🌟 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,且长度最长。

因为我们只允许移除数组的两端,所以保留的中间部分必须连续,这就是典型的滑动窗口应用场景。


🧪 解题方法:滑动窗口寻找最长连续子数组

✅ 算法步骤

  1. 计算总和 total
  2. 目标转化为找出一段连续子数组,其和为 target = total - x
  3. 使用滑动窗口从左到右遍历,维护当前窗口的和 curr_sum
  4. curr_sum > target,窗口左端右移,直到和不大于 target
  5. 如果 curr_sum == target,更新当前最长子数组长度
  6. 遍历结束后,若存在满足条件的子数组,返回 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 的组合。但实现复杂,不如滑动窗口直观。


✅ 总结

这道题巧妙地将“从两端移除求和”的问题,转化为了“保留一段连续子数组”的问题,从而可以用经典的滑动窗口技巧在线性时间内解决,值得深入理解。

📌 技术要点

  • 问题转化为滑动窗口模型
  • 理解前缀 + 后缀移除 = 中间保留
  • 使用滑动窗口寻找最长连续子数组满足目标和
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值