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