React Router

008-删除有序数组中的重复项 (Remove Duplicates from Sorted Array)

LeetCode 第26题 - 删除有序数组中的重复项的详细题解

题目描述

给你一个 升序排列 的数组 nums,请你 原地 删除重复出现的元素,使每个元素 只出现一次,返回删除后数组的新长度。

元素的 相对顺序 应该保持 一致。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

示例

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

输入:nums = [0,0,1,1,1,2,2,3,3,4]
输出:5, nums = [0,1,2,3,4,_,_,_,_,_]
解释:函数应该返回新的长度 5 , 并且原数组 nums 的前五个元素被修改为 0, 1, 2, 3, 4 。不需要考虑数组中超出新长度后面的元素。

解题思路

使用双指针法,一个指针 slow 指向当前唯一元素的最后一个位置,另一个指针 fast 遍历数组。当 fast 指向的元素与 slow 指向的元素不同时,将 fast 指向的元素移动到 slow + 1 的位置。

JavaScript 解决方案

/**
 * @param {number[]} nums
 * @return {number}
 */
const removeDuplicates = (nums) => {
    if (nums.length === 0) return 0;
    
    let slow = 0;
    
    for (let fast = 1; fast < nums.length; fast++) {
        // 如果当前元素与前一个元素不同,则移动到下一个位置
        if (nums[fast] !== nums[slow]) {
            slow++;
            nums[slow] = nums[fast];
        }
    }
    
    return slow + 1;
};

复杂度分析

  • 时间复杂度:O(n),其中 n 是数组长度
  • 空间复杂度:O(1),只使用了常数额外空间

测试用例

// 测试函数
const testRemoveDuplicates = () => {
    const testCases = [
        {
            nums: [1, 1, 2],
            expected: 2,
            expectedArray: [1, 2]
        },
        {
            nums: [0, 0, 1, 1, 1, 2, 2, 3, 3, 4],
            expected: 5,
            expectedArray: [0, 1, 2, 3, 4]
        },
        {
            nums: [1, 2, 3],
            expected: 3,
            expectedArray: [1, 2, 3]
        },
        {
            nums: [1, 1, 1, 1],
            expected: 1,
            expectedArray: [1]
        },
        {
            nums: [],
            expected: 0,
            expectedArray: []
        },
        {
            nums: [1],
            expected: 1,
            expectedArray: [1]
        }
    ];

    testCases.forEach((testCase, index) => {
        const nums = [...testCase.nums]; // 复制数组避免修改原数组
        const result = removeDuplicates(nums);
        const resultArray = nums.slice(0, result);
        const passed = result === testCase.expected && 
                      JSON.stringify(resultArray) === JSON.stringify(testCase.expectedArray);
        
        console.log(`测试用例 ${index + 1}: ${passed ? '通过' : '失败'}`);
        console.log(`输入: nums = [${testCase.nums}]`);
        console.log(`期望: ${testCase.expected}, nums = [${testCase.expectedArray}]`);
        console.log(`实际: ${result}, nums = [${resultArray}]\n`);
    });
};

// 运行测试
testRemoveDuplicates();

关键点总结

  1. 双指针法:使用快慢指针来原地修改数组
  2. 原地操作:不创建新数组,直接在原数组上操作
  3. 相对顺序:保持元素的相对顺序不变
  4. 边界处理:空数组和单个元素的处理
  5. 返回值:返回唯一元素的数量

总结

这道题考察了以下重点:

  • 双指针技巧:理解快慢指针在数组操作中的应用
  • 原地算法:如何在不使用额外空间的情况下修改数组
  • 数组遍历:高效地遍历和处理数组元素
  • 边界处理:对各种边界情况的考虑

双指针法是最优解,时间复杂度为 O(n),空间复杂度为 O(1)。在实际面试中,建议先解释双指针的思路,然后实现算法。