移除元素 图解题解
快慢双指针,把要留的直接写到前面,O(n) 一遍原地移除所有目标值。
就像整理一排柜子,挑出要留下的物品:快指针逐个检查每格,凡是不该扔的,就直接放到慢指针指着的空位,慢指针再前进一格——不是先找出要删的再费力后移,而是把要保留的往前抄,扫一遍就整理好了前缀。
这道题到底在问什么
- 输入
- nums = [0,1,2,2,3,0,4,2,1,2], val = 2
- 输出
- 6,前 6 个为 [0,1,3,0,4,1]
最优解:一步一步想明白
- 3记住这句「slow 守保留区末尾、fast 找不等于 val 的值搬过来」,下面每一帧都在套它。
- 4快指针 fast 走到下标 0,值 3 不等于 val(2)——这是要保留的值,准备搬到保留区位置 0。
- 5把保留值 3 搬到 nums[0],慢指针 slow 前移到 1。保留区现在是 [3]。
- 6快指针 fast 走到下标 1,值 2 正好等于 val(2)——要移除的值,slow 不动,直接跳过。
- 7快指针 fast 走到下标 2,值 0 不等于 val(2)——这是要保留的值,准备搬到保留区位置 1。
- 8把保留值 0 搬到 nums[1],慢指针 slow 前移到 2。保留区现在是 [3,0]。
- 9快指针 fast 走到下标 3,值 2 正好等于 val(2)——要移除的值,slow 不动,直接跳过。
- 10快指针 fast 走到下标 4,值 4 不等于 val(2)——这是要保留的值,准备搬到保留区位置 2。
- 11把保留值 4 搬到 nums[2],慢指针 slow 前移到 3。保留区现在是 [3,0,4]。
- 12快指针 fast 走到下标 5,值 2 正好等于 val(2)——要移除的值,slow 不动,直接跳过。
- 13快指针 fast 走到下标 6,值 1 不等于 val(2)——这是要保留的值,准备搬到保留区位置 3。
- 14把保留值 1 搬到 nums[3],慢指针 slow 前移到 4。保留区现在是 [3,0,4,1]。
- 15快指针 fast 走到下标 7,值 5 不等于 val(2)——这是要保留的值,准备搬到保留区位置 4。
- 16把保留值 5 搬到 nums[4],慢指针 slow 前移到 5。保留区现在是 [3,0,4,1,5]。
- 17快指针 fast 走到下标 8,值 2 正好等于 val(2)——要移除的值,slow 不动,直接跳过。
- 18快指针 fast 走到下标 9,值 6 不等于 val(2)——这是要保留的值,准备搬到保留区位置 5。
- 19把保留值 6 搬到 nums[5],慢指针 slow 前移到 6。保留区现在是 [3,0,4,1,5,6]。
- 20快指针 fast 走到下标 10,值 2 正好等于 val(2)——要移除的值,slow 不动,直接跳过。
- 21快指针 fast 走到下标 11,值 7 不等于 val(2)——这是要保留的值,准备搬到保留区位置 6。
- 22把保留值 7 搬到 nums[6],慢指针 slow 前移到 7。保留区现在是 [3,0,4,1,5,6,7]。
- 23快指针扫完整个数组,慢指针停在 7。前 7 个 [3,0,4,1,5,6,7] 就是保留结果,返回长度 7(下标 7 之后的旧值不用管)。
⚠️ 容易写错的地方
✗ 错:把等于 val 的也搬过去
✓ 对:只搬不等于 val 的值
slow 只在遇到保留值时前进并写入,等于 val 的直接跳过、不占保留区
✗ 错:搬完忘了 slow++
✓ 对:写入 nums[slow] 后立即 slow++
slow 既是写入位也是已保留个数,不前进会把下一个保留值覆盖掉
✗ 错:返回 slow-1 或 slow+1
✓ 对:返回 slow
slow 扫完正好等于保留元素个数(下标 0..slow-1 共 slow 个)
完整代码(Python / C++ / Java)
Python
def removeElement(nums, val):
slow = 0 # 保留区下一个写入位
for fast in range(len(nums)):
if nums[fast] != val: # 不等于 val 才保留
nums[slow] = nums[fast] # 搬过来
slow += 1
return slow # 新长度C++
int removeElement(vector<int>& nums, int val){
int slow = 0; // 保留区下一个写入位
for (int fast = 0; fast < nums.size(); ++fast)
if (nums[fast] != val) // 不等于 val 才保留
nums[slow++] = nums[fast]; // 搬过来
return slow; // 新长度
}Java
public int removeElement(int[] nums, int val) {
int slow = 0; // 保留区下一个写入位
for (int fast = 0; fast < nums.length; fast++) {
if (nums[fast] != val) { // 不等于 val 才保留
nums[slow] = nums[fast]; // 搬过来
slow++;
}
}
return slow; // 新长度
}复杂度
时间
O(n)
fast 把数组扫一遍,每个元素只看一次
空间
O(1)
原地在 nums 上覆盖保留值,不开新数组
看不够?换成动画再走一遍
上面的推演每一步都对应一帧动画。点开交互动画版,能一步步看着 移除元素 的数据怎么变、指针怎么走,还能切 Python / Java / C++ 跟着练。
面试官可能追问
slow 和 fast 各代表什么?+
slow 指向「保留区」末尾的下一个写入位(也等于已保留的元素个数);fast 是扫描指针,逐个查看元素是不是要保留的值。
保留下来的元素顺序会变吗?+
不会。fast 从左到右扫,保留值按原相对顺序依次搬到前面,前 k 个的相对顺序和原数组一致。
想听吴师兄把这道题讲给你听?
文字版和动画都随便看。开通图解算法年卡,可以听吴师兄把 移除元素 一步步讲透(全站已上线 905 份讲透,持续扩充), 卡住的地方还有 AI 私教小欧就着动画帮你拆到懂。
把这道题真正拿下
本平台为独立第三方培训机构,与华为技术有限公司无任何关联;课程的服务内容与权益以购买协议为准,学习效果因个人情况而异。「华为 OD」「华为可信」等仅为对岗位与考试方向的客观描述,相关商标归各自权利人所有。