题意: 给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使得出现次数超过两次的元素只出现两次 ,返回删除后数组的新长度。 不要使用额外的数组空间,你必须在原地修改输入数组 并在使用 O(1) 额外空间的条件下完成。
题目链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/
视频链接:https://www.bilibili.com/video/BV18G5UzzE8c/
一、看到题目的第一想法
这道题是「有序数组去重,每个元素最多保留两次」,和经典的快慢指针去重题是同类型的变种题。
- 核心直觉:因为数组是有序的,重复元素一定是连续的,所以可以用快慢指针来实现「原地修改」,不需要额外空间。
- 快慢指针的角色定位 :
fast指针负责遍历整个数组,寻找符合条件的元素(也就是不会让重复次数超过 2 次的元素)。slow指针负责维护「有效数组」的末尾位置,记录下一个可以存放新元素的位置。
- 边界条件预判:当数组长度 ≤ 2 时,不管元素是否重复,都可以直接返回原长度,因为最多也只有两个元素,天然满足 "不超过两次重复" 的要求。
二、实现过程中遇到的困难
- 慢指针起始位置的选择 一开始很容易沿用 "去重保留一次" 的写法,把
slow初始化为 1。但这道题允许保留两次,前两个元素天然有效,所以slow应该从索引 2 开始,直接从第三个元素开始判断是否需要替换。 - 判断条件的误区 很容易错误地用
nums[slow-1]和nums[fast]比较,这样会误判 "连续三个相同元素" 的情况。正确的逻辑是:只要fast指向的元素,和slow维护的有效数组中倒数第二个元素不同,就说明它还没出现过三次,可以加入有效数组 。也就是nums[slow-2] != nums[fast]。 - 指针移动的顺序问题 一开始可能会把
++fast写在if分支里,导致循环中fast没有每次都移动,出现死循环或者漏遍历的情况。fast指针必须在每次循环中都移动,只有满足条件时才移动slow指针。 - 对 "原地修改" 的理解偏差一开始可能会想着额外开一个数组存结果,忽略了题目要求的「原地修改」和「O (1) 额外空间」限制,而快慢指针刚好能满足这一点。

三、今日收获心得
- 快慢指针的通用模板思想 这类「有序数组去重 / 移除指定元素」的题目,快慢指针是通用解法:
fast遍历原数组,筛选有效元素。slow维护有效数组的写入位置,原地覆盖修改。这道题只是把 "保留一次" 的逻辑改成了 "保留两次",核心框架没有变。
- 有序数组的隐藏优势有序数组的重复元素是连续的,这让我们不需要额外的哈希表统计次数,只需要通过和前面固定位置的元素比较,就能判断重复次数,大大优化了空间复杂度。
- 边界条件的重要性 一开始处理
numsSize <= 2的情况,不仅能减少后续循环的无效执行,也避免了数组越界的问题,写算法题时先处理边界条件是个好习惯。 - 条件判断的本质理解 这道题的核心条件
nums[slow-2] != nums[fast],本质是在判断「当前元素是否已经在有效数组中出现了两次」,而不是简单的和前一个元素比较。理解了这一点,就可以轻松扩展到 "允许重复 k 次" 的通用场景(只需要把slow-2改成slow-k即可)。