#26 双指针 简单

删除有序数组中的重复项

在 LeetCode 上查看 ↗

🎧 语音讲解

开车或通勤时可听,跟着思路走一遍

速度

📋 题目描述

给你一个 非严格递增排列 的数组 nums,请你原地删除重复出现的元素,使每个元素 只出现一次,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致

考虑 nums 的唯一元素的数量为 k。去重后,返回唯一元素的数量 knums 的前 k 个元素应包含排序后的唯一数字,下标 k - 1 之后的剩余元素可以忽略。

示例 1

输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
函数应返回新长度 2,原数组前两个元素被修改为 1, 2。不需要考虑超出新长度后面的元素。

示例 2

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4,_,_,_,_,_]
函数应返回新长度 5,原数组前五个元素被修改为 0, 1, 2, 3, 4。

💭 模拟答题者思考

1. 我先想暴力:开一个新数组 res,从左到右扫 nums,遇到与 res 末尾不同的就 append——逻辑对,但用了 O(n) 额外空间,题目要求原地修改。

2. 重复在哪里?数组已排序,相同元素一定挨在一起。去重时只需关心「当前已写入的最后一个唯一值」,不必与前面所有元素逐一比较。

3. 双指针:用 slow 标记「去重结果区」的末尾,用 fast1 开始扫描。若 nums[fast] != nums[slow],说明遇到新唯一值,扩展结果区并写入。

4. 为什么只比 nums[slow]?有序性保证:若 nums[fast]nums[slow] 相等,则 nums[fast] 一定是重复;若不等,则 nums[fast] 一定大于 nums[slow],是新唯一值。

5. 最终 nums[0..slow] 即为去重结果,返回 slow + 1。每个元素最多被访问一次,时间 O(n),仅用两个下标,空间 O(1)。

🧠 变量语义(先读这三句再编码)

变量类型语义(三句法)
slowint定义:已写入去重结果区的最后一个唯一元素下标,即 nums[0..slow] 为当前已确认的唯一前缀
维护:初始 slow = 0(第一个元素天然唯一);每发现新值时先 slow++ 再写入
更新:当 nums[fast] != nums[slow] 时,slow += 1; nums[slow] = nums[fast]
fastint定义:扫描指针,从 1n-1 遍历整个数组
维护:每次只与 nums[slow] 比较(有序数组下重复项必相邻,无需回看更早位置)
更新:每轮循环末尾 fast++,直到遍历完所有元素
k(返回值)int定义:去重后唯一元素个数
维护:等于 slow + 1(下标从 0 起,长度 = 最后下标 + 1)
更新:循环结束后直接返回,无需额外计数器

⌨️ 落码步骤

1. 若 nums 为空,直接返回 0;否则令 slow = 0nums[0] 作为第一个唯一元素)

2. for fast in range(1, len(nums)):若 nums[fast] != nums[slow],则 slow += 1nums[slow] = nums[fast]

3. 循环结束,nums[0..slow] 为去重后的唯一前缀

4. 返回 slow + 1 作为唯一元素个数 k

💻 代码实现

class Solution:
    def removeDuplicates(self, nums: List[int]) -> int:
        if not nums:
            return 0

        slow = 0  # nums[0..slow] 为当前已确认的唯一前缀
        for fast in range(1, len(nums)):
            if nums[fast] != nums[slow]:
                slow += 1
                nums[slow] = nums[fast]

        return slow + 1
class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        if (nums.empty()) return 0;

        int slow = 0;  // nums[0..slow] 为当前已确认的唯一前缀
        for (int fast = 1; fast < nums.size(); fast++) {
            if (nums[fast] != nums[slow]) {
                slow++;
                nums[slow] = nums[fast];
            }
        }
        return slow + 1;
    }
};
// 时间 O(n),空间 O(1)

📈 复杂度分析

时间复杂度 O(n)
空间复杂度 O(1)

⚠️ 常见坑

返回值是 slow + 1 而不是 slow:下标从 0 开始,长度 = 最后下标 + 1。

写入顺序错误:应先 slow++ 再赋值 nums[slow] = nums[fast],否则会覆盖尚未保留的唯一元素。

fast 应从 1 开始而非 0:nums[0] 已作为初始唯一元素,从 0 开始会把自己与自己比较,逻辑冗余且易错。

🔍 必测边界 Case

Case 1:单元素
nums = [1] → 1, nums = [1](无需去重,直接返回 1)
Case 2:全部相同
nums = [2,2,2,2] → 1, nums = [2,_,_,_]fast 扫完无新值,slow 始终为 0)
Case 3:无重复
nums = [1,2,3,4] → 4, nums = [1,2,3,4](每个元素都被写入,slow 最终为 3)