
Problem Statement
给一个非递减排序的整数数组 nums,原地删除重复项,使每个元素只出现一次。返回唯一元素的数量 k,且前 k 个位置放排序好的唯一值。
示例:
[1,1,2]→k=2, nums=[1,2,_][0,0,1,1,1,2,2,3,3,4]→k=5, nums=[0,1,2,3,4,_,_,_,_,_]
My Solution
原始思路(收到提示前)
创建一个整数向量存储唯一的数值。创建两个指针指向首节点。先判断首节点是不是空,是空就返还0和原数组……指针1移动到当前的next节点……当循环结束,重新将前k位的值替换成向量里面的数。
问题:这是数组不是链表,没有”节点”、”next”、”空节点”这些概念。而且创建额外向量违反了”原地”的要求。
提示后的新思路
关键洞察:数组已排序 → 重复值都挨在一起。只需遍历时跳过相邻重复即可。
代码(C++)
1 |
|
思路详解
遍历数组,发现相邻重复就 erase 当前元素,然后 i-- 回退重新检查同一位置。直观理解:每次看到两个相邻的相同数字,就删掉前面那个。
i-- 的正确性:i 变成 -1 是短暂的中间状态,循环体结束后 for 的 i++ 立刻把它拉回 0,不会造成数组越界访问。
调试过程
用 [0,0,1,1,1,2,2,3,3,4] 逐步跟踪:
1 | i=0 [0,0,1,1,1,2,2,3,3,4] 删除索引0 |
最终 [0,1,2,3,4],k=5。
复杂度分析
- 时间:O(n²),每次
erase触发后续元素搬移,最坏情况(全重复)退化为平方级 - 空间:O(1),原地操作
最优解(O(n) 双索引)
不使用 erase,而是用一个慢指针 k 记录写入位置,快指针 i 扫描全数组:
1 | int removeDuplicates(vector<int>& nums) { |
k 既是唯一元素个数,也是下一个写入位置。不需要删除任何元素,直接覆盖。
审查发现
| 等级 | 维度 | 问题 | 建议 |
|---|---|---|---|
| 🔴 | 复杂度 | erase 导致 O(n²),最优 O(n) |
改用双索引覆盖 |
| 🟡 | 质量 | i-- 回退虽正确但不够直观 |
双索引写法更干净,一次遍历 |
| 🟢 | 正确性 | 边界(空数组、单元素、全重复)均正确 | — |
同类题目
- #27 Remove Element — 同样双指针覆盖思路,去掉指定值
- #80 Remove Duplicates from Sorted Array II — 升级版,允许每个元素出现两次