将x减到0的最小操作数
- 个人总结的八步归纳
- AI的归纳
- **8步归纳法(极简直白版)**
- 1. 问题本质
- 2. 问题特征
- 3. 切入点
- 4. 解决流程
- 5. 每步目标与操作
- 6. 注意事项
- 7. 最终目标
- 8. 整体总结
- 代码对照(逐行解析)
- 举个栗子🌰
- **一句话总结**
题目链接:
将x减到0的最小操作数
题目描述:
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
如果可以将 x 恰好 减到 0 ,返回 最小操作数 ;否则,返回 -1 。
示例 1:
输入:nums = [1,1,4,2,3], x = 5
输出:2
解释:最佳解决方案是移除后两个元素,将 x 减到 0 。
示例 2:
输入:nums = [5,6,7,8,9], x = 4
输出:-1
示例 3:
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
提示:
1 <= nums.length <= 105
1 <= nums[i] <= 104
1 <= x <= 109
题目转化:
这个题目的要求是求出数组左右两边相加等于x的最短长度的和
也就是:
找出最长的子数组的长度
这个子数组的所有元素的和正好等于sum - x
sum是整个数组的所有元素的和
和之前讨论过的滑动窗口一模一样:
我们这里就直接使用滑动窗口来解决这道题目:
个人总结的八步归纳
第一步:用自己的话,描述一下眼前的这个问题,它是一个怎么样的问题?
要求的是数组左右两边的和等于X的最小长度
第二步:这个问题有哪些特征,能让我们去判断,它属于这一类的问题?
正难则反,可以涉及到连续的长度子数组,以及求最短,最长的长度,这道题目就可以转换为求出一个子数组,要求在这个连续的子数组的和是等于 总数组的和减去x的,最后返回总数组的长度减去子数组的长度,如果这个子数组的和不能等于sum-x,那么就返回-1。
第三步:想要解决这类问题,切入点啥,即第一步我们要从哪里开始?
切入在在于转化为求一个连续子数组的最长的长度,这个子数组的和等于sum-x
第四步:解决这个问题的流程是怎么样的?这里说的流程是指,这道题可以分成12345…步,只要按照12345这个顺序做下去,我们就能解决这个问题。
- 定义一个ret变量,初始化为-1,记录子数组的最长长度
- 定义一个sum记录总数组的和,n记录总数组的长度
- 定义一个target记录sum - x,如果这个target小于0,那返回-1
- 定义一个temp记录这个子数组的和
- 使用同向双指针去扩展temp的右窗口
- 如果temp 的值大于了target,那就左缩窗口
- 如果temp 刚好等于target,那就更新re的结果为最大的长度
- 最后,如果ret= -1,说明没有这个连续子数组没有刚好等于target的值,那么就返回-1
- 如果ret不是等于-1,那么就返回n - ret
第五步:在流程的12345步中,每一步的目标是什么(就是要求到些什么)?每一步需要用到哪些知识
- ret为了记录子数组的最长长度
- sum 记录总数组长度的值
- target记录sum -x的值
- temp记录子数组的元素总和
- 开始使用滑动窗口去扩展temp的值
- 当temp的值大于target时,就要左缩窗口,当temp=target时,更新ret的结果
- 如果ret=-1,那就说明没有子数组的和正好等于sum-x
- 如果ret !=-1;那就说明有子数组的和刚好等于了sum-x
- 最后我们就返回n-ret即可
第六步:要思考在运用这些知识和技巧的时候,有些需要注意的地方。
- 记录target时,如果sum - x < 0了,那就直接返回了-1
- 使用的是temp来记录子数组的和,来带入滑动窗口中计算
- 当temp == target的时候,才会去更新结果
- 最后要返回的是-1或者是n-ret
第七步:要解决这个问题,最终的目标是啥?也就是说,我最终要求出的是啥?
最终目标是求出这个总数组中左右子数组的和正好等于x的最小长度
我们将这个目标转换为了:
求出这个总数组中的一个连续的子数组的和正好等于sum-x的最长长度
第八步:重新对上面的第一步至第七步,进行回顾和揣摩(包括问题类型,特征,切入点,解决过程,1234567…步,每步需要用的哪些知识方法)
(2)AI+8步归纳结合
在模仿完题目后,先自己用8步归纳法对错题进行归纳,然后让deepseek,按照8步归纳法的原则,对题目进行总结归纳,你再把自己总结归纳出的东西,跟AI对比,看看有哪些地方可以改进。
AI的归纳
8步归纳法(极简直白版)
1. 问题本质
从数组两端不断删除元素,使删除的元素和等于x,求最少删几次?若做不到,返回-1。
2. 问题特征
- 逆向思维:求两端删除次数最少 → 等价于求中间保留的连续子数组最长
- 数学转化:中间子数组的和 = 总和 - x
- 滑动窗口适用:数组元素全为正数,适合用双指针找连续子数组
3. 切入点
- 算数组总和
sum
- 算目标值
target = sum - x
- 若
target < 0
:直接返回-1(总和都不够x)
- 若
- 用滑动窗口找和为
target
的最长子数组
4. 解决流程
- 算总和:遍历数组求
sum
- 算目标:
target = sum - x
,若target < 0
返回-1 - 滑动窗口:
- 双指针
left=0
,currentSum=0
right
右移累加元素 →currentSum += nums[right]
- 当
currentSum > target
时,左移left
减元素 →currentSum -= nums[left++]
- 当
currentSum == target
时,记录窗口长度right-left+1
的最大值
- 双指针
- 结果处理:
- 若未找到 → 返回-1
- 若找到 → 最少操作次数 = 数组长度 - 最长子数组长度
5. 每步目标与操作
步骤 | 做什么 | 为什么这么做 |
---|---|---|
算总和 | 确定中间子数组的目标和 | 转化问题核心条件 |
滑动窗口 | 找和为target 的最长子数组 | 滑动窗口能高效处理连续子数组问题 |
结果转换 | 将子数组长度转为操作次数 | 原问题要求的是两端的操作次数 |
6. 注意事项
- 边界情况1:若
target = 0
(即sum = x
),需删除整个数组 → 返回n
- 边界情况2:数组全为1,x=5,但总和=3 → 直接返回-1
- 效率:每个元素最多被访问两次 → 时间复杂度O(n)
7. 最终目标
求最少删除次数 = 数组长度 - 中间最长子数组的长度
8. 整体总结
- 问题转化:删除两端 → 保留中间最长
- 核心操作:滑动窗口找和为
target
的最长子数组 - 代码框架:
计算总和 → 处理边界 → 滑动窗口找最长子数组 → 返回结果
代码对照(逐行解析)
class Solution {public int minOperations(int[] nums, int x) {int sum = 0;for (int num : nums) sum += num; // 1. 算总和int target = sum - x; // 2. 算目标值if (target < 0) return -1; // 边界:总和都不够xint maxLen = -1; // 记录中间最长子数组的长度int left = 0, currentSum = 0;// 3. 滑动窗口找最长子数组for (int right = 0; right < nums.length; right++) {currentSum += nums[right]; // 右指针扩张while (currentSum > target) // 窗口和太大,左指针收缩currentSum -= nums[left++];if (currentSum == target) // 找到合法窗口maxLen = Math.max(maxLen, right - left + 1);}// 4. 结果处理return maxLen == -1 ? -1 : nums.length - maxLen;}
}
举个栗子🌰
以示例1 nums = [1,1,4,2,3], x = 5
:
sum = 11
→target = 11-5 = 6
- 找和为6的最长子数组:
[1,1,4]
和=6 → 长度3- 或
[4,2]
和=6 → 长度2 - 最长长度为3
- 最少操作次数 = 5(总长) - 3 = 2
一句话总结
求两端删最少 → 中间留最长 → 滑动窗口找和为sum-x
的最长子数组