0026.删除有序数组中的重复项
难度:🟢 简单
标签:数组
、双指针
题目描述
给你一个 非严格递增排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums
中唯一元素的个数。
考虑 nums
的唯一元素的数量为 k
,你需要做以下事情确保你的题解可以被通过:
更改数组
nums
,使nums
的前k
个元素包含唯一元素,并按照它们最初在nums
中出现的顺序排列。nums
的其余元素与nums
的大小不重要。返回
k
。
示例 1:
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
示例 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
指向的值不同的元素时,就意味着发现了一个新的唯一元素。此时,我们将慢指针后移一位,并将快指针指向的新元素值赋给慢指针的新位置。
关键步骤
处理边界情况:如果数组为空,直接返回 0。
初始化指针:初始化慢指针
slow = 0
。循环遍历:用快指针
fast
从索引 1 开始遍历整个数组。比较与写入: a. 比较
nums[fast]
和nums[slow]
的值。 b. 如果它们 不相等,说明nums[fast]
是一个新的唯一元素。此时,先将慢指针slow
加一,然后将nums[fast]
的值赋给nums[slow]
。 c. 如果它们 相等,则慢指针slow
不动,快指针fast
继续前进,跳过这个重复项。返回结果:遍历结束后,
slow
指针的位置是最后一个唯一元素的索引。因此,新数组的长度(即唯一元素的个数)为slow + 1
。
代码实现
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function (nums) {
if (nums.length === 0) {
return 0;
}
// 慢指针,指向新数组的末尾
let slow = 0;
// 快指针,遍历整个数组
for (let fast = 1; fast < nums.length; fast++) {
// 如果快慢指针指向的值不同,说明遇到了新元素
if (nums[slow] !== nums[fast]) {
slow++;
nums[slow] = nums[fast];
}
}
// 新数组的长度为慢指针索引 + 1
return slow + 1;
};
复杂度分析
时间复杂度:O(n) 其中 n 是数组
nums
的长度。快慢指针都只对数组进行一次完整的遍历。空间复杂度:O(1) 我们是在原数组上进行操作,只使用了常数个额外变量作为指针,空间消耗是恒定的。
相关题目
总结
本题是双指针思想在原地修改数组问题中的一个经典范例。通过“一个指针管写入,一个指针管读取”的模式,可以高效地完成元素的筛选和覆盖,是处理有序数组去重或元素移除问题的标准解法。