删除有序数组中的重复项
文章目录
一、题目
给你一个 升序排列 的数组 nums
,请你 原地 删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。
由于在某些语言中不能改变数组的长度,所以必须将结果放在数组nums的第一部分。更规范地说,如果在删除重复项之后有 k
个元素,那么 nums
的前 k
个元素应该保存最终结果。
将最终结果插入 nums
的前 k
个位置后返回 k
。
不要使用额外的空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。
示例
输入:nums = [1,1,2]
输出:2, nums = [1,2,_]
解释:函数应该返回新的长度 2 ,并且原数组 nums 的前两个元素被修改为 1, 2 。不需要考虑数组中超出新长度后面的元素。
二、题解
方式1(改变数组长度)
- 遍历数组,判断当前元素是否等于下一个元素,如果等于,就直接删除当前元素
- 由于重复的元素会直接被删除,所以这种方法会改变数组长度
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
let i = 0
while (nums[i+1] !== undefined) {
if (nums[i] === nums[i + 1]) {
nums.splice(i,1)
} else {
i++
}
}
return nums.length
};
这种方法虽然理解起来很简单,但性能感人。。
因为频繁删除数组元素非常耗费性能,详细原因在文末
方式2(不改变数组长度)
来自力扣的解法~~
思路
采用快慢指针
数组是有序的,那么重复的元素一定会相邻。在同一个数组里面操作,把不重复的元素移到数组的左侧,最后取左侧的数组的值。
步骤
-
比较
fast
和low
位置的元素是否相等。
○ 循环执行:
如果相等,fast
后移 1 位
如果不相等,将low
前一位的值改为fast
,low
后移1位,fast
后移 1 位○ 循环结束:
fast
越界 -
循环结束,返回新数组长度
low + 1
/**
* @param {number[]} nums
* @return {number}
*/
var removeDuplicates = function(nums) {
// 判断边界
if (nums && nums.length < 0) {
return 0
}
var low = 0, fast = 1, n = nums.length;
while (fast < n) {
if (nums[fast] !== nums[low]) {
nums[low + 1] = nums[fast]
low++
}
fast++
}
return low + 1
};
三、拓展
为什么频繁删除数组元素,会很耗费性能呢?
参考自浅谈JS数组在内存中的效率问题
1、我们所熟知的JS数组的结构和其他语言数组结构类似,即物理内存是连续的,所以这也就导致了数组成员移动次数越少,其效率越高,一般数组会被分配到一段连续的内存中,像这样
其实我们的数组在定义的时候默认了它的长度,即当我们想要在数组中添加或删除元素的时候,就需要对它的长度做出改变。
2、我们看到的仅仅只是数组中的一个元素被删除,其实底层数组中,这个元素后面的所有成员都需要向前移动,当移动元素非常多时,性能就会很差
额外补充:非连续内存问题
- 我们都知道JS数组是可以在同一数组中存储不同数据类型的数据的。
- 所以就造成了JS的数组是否分配连续内存,是要看我们的数组成员类型
- 当数组内成员为同一类,则为连续内存
- 当数组成员存在数组,字符串,那么就会分配非连续的内存
- 多个非连续内存会形成一个链表,当存储数据数量级庞大的时候,就会造成效率低下的问题。