问题描述

第一次解题存在问题<方法2中while条件注释部分–问题点:缺少等号>
class Solution {
public int removeElement(int[] nums, int val) {
//方法1
//思路:利用双指针+选择排序
//从头遍历,不相等则交换
//p1为for循环时i后面(包含i)的第一个不等于val的位置
int p1=0;
for(int i=0;i<nums.length;i++){
if(nums[i]!=val){
nums[p1]=nums[i];
p1++;
}
}
return p1;
//方法2 优化双指针
int p1=0;
int p2=nums.length-1;
int current = 0;
//while(p1<p2){ //第一遍解题答案中,判定条件为p1<p2,缺少=
while(p1<=p2){
if(nums[p1]==val){
nums[p1]=nums[p2];
p2--;
}else{
p1++;
}
}
return p1;
}
}
第一次解题错误的问题和原因
问题代码(注释部分)
//while(p1<p2){ //第一遍解题答案中,判定条件为p1<p2,缺少=
while(p1<=p2){
if(nums[p1]==val){
nums[p1]=nums[p2];
p2--;
}else{
p1++;
}
}
导致结果

原因:
p1 < p2而不是p1 < =p2时:
移动方式:当 nums[p1]==val 时,只移动 p2 不移动 p1,当前位置仍需要处理
中间元素处理:当 p1 == p2 时,这个元素尚未处理
边界测试:单元素数组 [2], val=2:应返回长度0,p1=0, p2=0 → 如果不用 <=,循环根本不执行
技术细节
小结
解决边界判断难题:while 循环条件中是否加等号?
在双指针算法中,判断循环条件是使用 left < right 还是 left <= right 是一个常见痛点
判断技巧 - “三看原则”
第一看:看指针移动方式
| 移动特点 | 推荐条件 | 例子 |
|---|---|---|
| 移动后直接跳过当前元素(如 left++ 或 right–) | left <= right | 二分查找:left = mid + 1 |
| 移动后仍处理当前元素(如不移动或移动但不跳过) | left < right | 快慢指针:slow = slow.next |
第二看:看终止时中间元素是否需处理
| 中间元素状态 | 推荐条件 | 例子 |
|---|---|---|
| 相遇点需要处理 | left <= right | 移除元素中需检测最后单个元素 |
| 相遇点无需处理 | left < right | 两数之和中左右指针不能重合 |
第三看:看边界测试用例
用特殊边界值验证循环条件:
1.空数组测试:[]
2.单元素数组:[1]
3.双元素数组:[1,2]
决策流程图

通用解决方案 - 调试模板
当不确定循环条件时,使用这个调试模板:
int left = ..., right = ...;
// 先用最保险的条件
while (left <= right) { // 保险包含等号
// 核心逻辑...
// 增加安全保护
if (left == right) {
// 处理最后单个元素
// ...
break; // 处理后退出
}
}
常见算法条件总结表
| 算法类型 | 推荐条件 | 解释 |
|---|---|---|
| 二分查找 | left <= right | 需处理最后单个元素 |
| 两数之和 | left < right | 左右指针不能重合 |
| 快慢指针 | fast != null && fast.next != null | 防止空指针异常 |
| 滑动窗口 | right < nums.length | 右指针遍历到结尾 |
| 原地移除元素 | left <= right | 处理最后单个元素 |
终极心法
当犹豫不决时,先用 left <= right 确保中间元素被处理,然后通过测试用例验证是否能优化为 left < right
通过系统化分析指针移动方式、中间元素处理需求和边界测试,结合调试模板,就能在双指针算法中处理循环条件问题
--引出边界判断难题:while 循环条件中是否加等号的问题&spm=1001.2101.3001.5002&articleId=149404191&d=1&t=3&u=d0f64cdc30aa44ff96a5076be0085a15)
505

被折叠的 条评论
为什么被折叠?



