【详细解析】删除有序数组中的重复项 II

一、问题描述

1. 核心要求

给定一个升序排列 的数组 nums,需要原地删除重复出现的元素,使得每个元素最多出现两次,返回删除后数组的新长度。

  • 空间复杂度要求:O(1)(不能使用额外数组);

  • 输入数组以"引用"传递,修改需对调用者可见;

  • 无需处理新长度之外的数组元素。

2. 示例分析

输入数组 输出长度 输出数组(前N位) 说明
1,1,1,2,2,3 5 1,1,2,2,3 数字1重复3次,保留2次
0,0,1,1,1,1,2,3,3 7 0,0,1,1,2,3,3 数字1重复4次,保留2次

3. 关键约束

  • 数组长度范围:1 ≤ nums.length ≤ 3×10⁴;

  • 数组元素范围:-10⁴ ≤ numsi ≤ 10⁴;

  • 数组已按升序排列(核心前提,可利用有序性简化逻辑)。

二、解题思路

1. 核心逻辑:双指针法

由于数组是有序的,重复元素必然连续出现,因此可以用快慢指针实现原地修改:

  • 慢指针(slow):标记新数组的末尾位置(即最终保留的元素的最后一位);

  • 快指针(fast):遍历原数组,检查当前元素是否符合"最多出现两次"的条件。

2. 条件判断规则

对于快指针指向的元素 nums[fast],只需判断:

nums[fast] 是否与慢指针前两位的元素 nums[slow-2] 相等。

  • 若不相等:说明当前元素可保留,将其赋值给 nums[slow],慢指针右移;

  • 若相等:说明当前元素已出现两次以上,跳过该元素,快指针继续右移。

3. 边界处理

  • 当数组长度 ≤ 2 时,无需删除任何元素,直接返回原长度;

  • 慢指针初始值为 2(前两位元素必然保留),快指针从 2 开始遍历。

三、完整代码实现

Python 复制代码
from typing import List

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        # 边界条件:数组长度≤2时,直接返回原长度
        if len(nums) <= 2:
            return len(nums)
        
        # 慢指针:初始为2(前两位必然保留)
        slow = 2
        # 快指针:从2开始遍历数组
        for fast in range(2, len(nums)):
            # 核心判断:当前元素与慢指针前两位元素不同,说明可保留
            if nums[fast] != nums[slow - 2]:
                nums[slow] = nums[fast]
                slow += 1
        
        # 慢指针的最终位置即为新数组长度
        return slow

四、代码核心解析

1. 边界条件处理

Python 复制代码
if len(nums) <= 2:
    return len(nums)
  • 当数组长度≤2时,无论元素是否重复,都满足"最多出现两次"的要求,直接返回原长度,避免无效计算。

2. 双指针初始化

Python 复制代码
slow = 2
for fast in range(2, len(nums)):
  • 慢指针 slow 初始化为 2:前两位元素(索引0、1)必然保留,无需检查;

  • 快指针 fast 从 2 开始遍历:只需检查第三位及以后的元素。

3. 核心判断与赋值

Python 复制代码
if nums[fast] != nums[slow - 2]:
    nums[slow] = nums[fast]
    slow += 1
  • 关键逻辑:利用数组的有序性,只需对比当前元素与新数组倒数第二位(slow-2)是否相等,即可判断是否超过两次;

  • 赋值操作:将符合条件的元素"覆盖"到慢指针位置,实现原地修改;

  • 慢指针右移:标记新数组的下一个空位。

4. 返回值

Python 复制代码
return slow
  • 慢指针的最终位置即为新数组的长度(因为慢指针始终指向新数组的下一个空位)。

五、测试用例验证

测试用例1:nums = 1,1,1,2,2,3

遍历步骤 fast numsfast numsslow-2 条件判断 slow nums 数组变化
初始 - - - - 2 1,1,1,2,2,3
1 2 1 nums0=1 相等 2 无变化
2 3 2 nums0=1 不相等 3 1,1,2,2,2,3
3 4 2 nums1=1 不相等 4 1,1,2,2,2,3
4 5 3 nums2=2 不相等 5 1,1,2,2,3,3
  • 最终返回 slow=5,数组前5位为 [1,1,2,2,3],符合示例要求。

测试用例2:nums = 0,0,1,1,1,1,2,3,3

遍历步骤 fast numsfast numsslow-2 条件判断 slow nums 数组变化
初始 - - - - 2 0,0,1,1,1,1,2,3,3
1 2 1 nums0=0 不相等 3 0,0,1,1,1,1,2,3,3
2 3 1 nums1=0 不相等 4 0,0,1,1,1,1,2,3,3
3 4 1 nums2=1 相等 4 无变化
4 5 1 nums2=1 相等 4 无变化
5 6 2 nums2=1 不相等 5 0,0,1,1,2,1,2,3,3
6 7 3 nums3=1 不相等 6 0,0,1,1,2,3,2,3,3
7 8 3 nums4=2 不相等 7 0,0,1,1,2,3,3,3,3
  • 最终返回 slow=7,数组前7位为 [0,0,1,1,2,3,3],符合示例要求。

测试用例3:nums = 1,2,3

  • 数组长度=3,slow初始为2,fast遍历2:

    • nums2=3 ≠ nums0=1 → slow=3;
  • 返回3,数组无变化,符合预期。

六、复杂度分析

时间复杂度

  • 仅遍历数组一次(fast指针从2到len(nums)-1),时间复杂度为 O(n)(n为数组长度);

  • 所有操作均为常数级,无嵌套循环,效率最优。

空间复杂度

  • 仅使用了slow、fast两个指针变量,无额外数组或容器,空间复杂度为 O(1),满足题目要求。

七、总结

关键点回顾

  1. 双指针核心:慢指针标记新数组边界,快指针遍历检查,利用有序性简化重复判断;

  2. 条件简化 :无需统计元素出现次数,只需对比当前元素与新数组倒数第二位(slow-2),即可判断是否超过两次;

  3. 原地修改:通过覆盖赋值实现O(1)空间复杂度,符合题目核心要求;

  4. 边界处理:提前处理数组长度≤2的情况,避免逻辑冗余。

该方法是解决"有序数组去重"类问题的经典思路,通过优化判断条件,既保证了逻辑简洁性,又实现了最优的时间和空间复杂度,可推广到"保留k次重复"的通用场景(只需将判断条件中的slow-2改为slow-k)。

相关推荐
BothSavage8 小时前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn8 小时前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法
烬羽10 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
先吃饱再说1 天前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰1 天前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术1 天前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六1 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术1 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize1 天前
初识DFS 与 BFS:递归、队列与图遍历
算法