LeetCode 1658 | 将 x 减到 0 的最小操作数(C语言滑动窗口解法)
💡LeetCode 1658 | 将 x 减到 0 的最小操作数(C语言滑动窗口解法)
📅 作者:凡间的八戒
🧠 标签:LeetCodeC语言算法双指针滑动窗口数组
🚀 一、题目描述
给你一个整数数组 nums 和一个整数 x。每一次操作,你可以从数组的 最左侧 或 最右侧 移除一个元素,并将该元素的值从 x 中减去。
请返回将 x 恰好减到 0 所需的最小操作数;如果无法做到,返回 -1。
🔹 示例 1:
输入:
nums = [1,1,4,2,3], x = 5
输出:
2
解释:
最佳方案是移除右侧的 [2,3],共两次操作,使得 x 从 5 变为 0。
🔹 示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1
🔹 示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳方案是移除前 3 个元素和后 2 个元素,总共 5 次操作。
🔹 数据范围:
1 <= nums.length <= 10^5
1 <= nums[i] <= 10^4
1 <= x <= 10^9
💭 二、思路分析
题目看似是“左右两边取数”的操作题,但其实可以转化为一个更容易处理的 子数组问题:
✅ 转化思路:
把「从两端取」转化为「保留中间一段」。
假设数组 nums 的总和为 sum,要从两端移除若干元素使得剩余的数和为 sum - x。
因此,问题就变成了:
在数组中找到一个 和为
sum - x的最长子数组,那么需要移除的元素数就是n - len(该子数组)。
✨ 举个例子:
nums = [1,1,4,2,3], x = 5
sum = 11
sum - x = 6
我们要找到一个子数组和为 6,比如 [1,4,1],长度为 3,那么最少操作数是:
n - len = 5 - 3 = 2
⚙️ 三、算法实现(C语言)
使用 滑动窗口 + 双指针 实现,时间复杂度 O(n)。
#define MIN(a, b) ((a) < (b) ? (a) : (b))int minOperations(int* nums, int numsSize, int x) {int sum = 0;for (int i = 0; i < numsSize; i++)sum += nums[i];if (sum < x) return -1; // 无法凑到xif (sum == x) return numsSize; // 必须全部移除int target = sum - x;int left = 0, right = 0;int cur = 0, maxLen = -1;while (right < numsSize) {cur += nums[right++];while (cur > target) {cur -= nums[left++];}if (cur == target) {maxLen = (maxLen > (right - left)) ? maxLen : (right - left);}}return maxLen == -1 ? -1 : numsSize - maxLen;
}
🧪 四、测试结果
| 测试用例 | 输入 | 输出 | 说明 |
|---|---|---|---|
| Case 1 | [1,1,4,2,3], x=5 | 2 | 移除 [2,3] |
| Case 2 | [5,6,7,8,9], x=4 | -1 | 无法满足 |
| Case 3 | [3,2,20,1,1,3], x=10 | 5 | 移除前3后2 |
💚 所有样例均通过,运行时间 0ms。
🔍 五、复杂度分析
- 时间复杂度: O(n),每个元素最多被左右指针访问一次
- 空间复杂度: O(1),仅使用常数级辅助变量
🧩 六、思考与总结
这道题是典型的「从两端取数 → 滑动窗口转化」的思路题。
在面对这类题目时:
-
思维转化很关键:
不直接操作两端,而是考虑中间子数组的保留。 -
滑动窗口的灵活性:
利用双指针动态维护窗口区间,轻松求解最长连续子数组。 -
代码实现清晰简洁:
注意边界情况(sum < x、sum == x)的提前返回。
🧠 七、延伸思考
这种思路还可以推广到:
- 最长连续子数组求和问题
- 前缀和 + 双指针优化
- 滑动窗口在数组中的通用应用
✍️ 总结一句话:
这道题的关键不是数学计算,而是“思维转化 + 窗口技巧”。
👉 掌握滑动窗口思想,很多看似复杂的双端题都能迎刃而解!
