问题描述
给你一个有序数组 nums
,请你原地删除重复出现的元素,使得出现次数超过两次的元素只出现两次,返回删除后数组的新长度。
不要使用额外的数组空间,你必须在原地修改输入数组并在使用 O(1) 额外空间的条件下完成。
示例 1:
1 | 输入:nums = [1,1,1,2,2,3] |
示例 2:
1 | 输入:nums = [0,0,1,1,1,1,2,3,3] |
约束条件:
1 <= nums.length <= 3 * 10^4
-10^4 <= nums[i] <= 10^4
nums
已按非严格递增顺序排列
解题思路
这道题是经典的双指针问题,核心思想是维护一个"有效区域",确保每个元素最多出现两次。
核心洞察
关键洞察:对于位置 left
,如果 nums[left-2] != nums[right]
,那么 nums[right]
可以安全地放入位置 left
,因为这保证了最多连续两个相同元素。
算法步骤
- 初始化:由于前两个元素无论如何都可以保留,所以从索引 2 开始处理
- 双指针遍历:
left
:指向下一个要填入的位置right
:遍历整个数组
- 判断条件:比较
nums[left-2]
和nums[right]
- 如果不相等,说明
nums[right]
可以放入,因为它与前面第二个元素不同 - 如果相等,说明已经有两个相同元素了,跳过当前元素
- 如果不相等,说明
可视化示例
以 nums = [1,1,1,2,2,3]
为例:
1 | 初始状态: [1,1,1,2,2,3] |
代码实现
1 | func removeDuplicates(nums []int) int { |
执行过程分析
以 nums = [0,0,1,1,1,1,2,3,3]
为例:
1 | 初始: [0,0,1,1,1,1,2,3,3] left=2, right=2 |
复杂度分析
时间复杂度:$O(n)$
- 只需要遍历数组一次,每个元素最多被访问两次
空间复杂度:$O(1)$
- 只使用了常数个额外变量,原地修改数组
关键收获
核心技巧
- 双指针模式:一个指针负责遍历,另一个指针维护有效区域
- 前瞻比较:通过比较
nums[left-2]
和nums[right]
来判断是否可以保留元素 - 原地修改:直接在原数组上操作,节省空间
常见陷阱
- 边界处理:忘记处理数组长度小于等于 2 的情况
- 索引越界:
left-2
可能越界,需要从索引 2 开始 - 理解错误:误以为需要计数每个元素的出现次数
扩展应用
- 可以轻松扩展到"最多保留 k 个重复元素"的情况
- 类似的原地修改数组问题都可以使用双指针技术
- 这种模式在处理有序数组时特别有效