「前端刷题」80. 删除有序数组中的重复项 II

258 阅读2分钟

「这是我参与11月更文挑战的第18天,活动详情查看:2021最后一次更文挑战」。

题目

链接:leetcode-cn.com/problems/re…

给你一个有序数组 nums ,请你 原地 删除重复出现的元素,使每个元素 最多出现两次 ,返回删除后数组的新长度。

不要使用额外的数组空间,你必须在 原地 修改输入数组 并在使用 O(1) 额外空间的条件下完成。

 

说明:

为什么返回数值是整数,但输出的答案是数组呢?

请注意,输入数组是以**「引用」**方式传递的,这意味着在函数里修改输入数组对于调用者是可见的。

你可以想象内部操作如下:

// nums 是以“引用”方式传递的。也就是说,不对实参做任何拷贝 int len = removeDuplicates(nums);

// 在函数里修改输入数组对于调用者是可见的。 // 根据你的函数返回的长度, 它会打印出数组中 该长度范围内 的所有元素。 for (int i = 0; i < len; i++) {     print(nums[i]); }

 

示例 1:

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

示例 2:

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

 

提示:

  • 1 <= nums.length <= 3 * 104
  • -104 <= nums[i] <= 104
  • nums 已按升序排列

解题思路

思路1

题目要求: 每个元素 最多出现两次

循环一遍数组nums, 用m记录数字出现的次数, j记录替换位置, 当 当前和下一个不相等时, 替换nums[j]nums[i]

直到循环结束, 此时j为处理后的数组长度, 直接截断

image.png

i = 0, m = 1, j = 0, nums = [1,1,1,2,2,3]

i = 1, m = 2, j = 0, nums = [1,1,1,2,2,3]
i = 2, m = 3, j = 0, nums = [1,1,1,2,2,3]
i = 3, m = 1, j = 2, nums = [1,1,1,2,2,3]
i = 4, m = 2, j = 2, nums = [1,1,1,2,2,3]
i = 5, m = 1, j = 4, nums = [1,1,2,2,2,3]
i = 6, m = 1, j = 5, nums = [1,1,2,2,3,3]
//截断
i = 6, m = 1, j = 5, nums = [1,1,2,2,3]
/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    let n = nums.length;
    if (!n) return n
    let j = 0
    let i = 0
    let m = 1
    while (i < n) {
        if (nums[i] !== nums[i + 1]) {
            if (m > 1) nums[j++] = nums[i] 
            nums[j++] = nums[i]
            m = 0
        }
        m++
        i++
    }
    return nums.length = j
}

思路2

快慢指针

image.png

var removeDuplicates = function(nums) {
    let n = nums.length;
    if (n < 3) return n
    let slow = 2
    let fast = 2
    while (fast < n) {
        if (nums[slow - 2] !== nums[fast]){
            nums[slow++] = nums[fast]
        }
        fast++
    }
    return nums.length = slow
};

思路3

设置快慢指针相同的起始位置,如果在移动的过程中一直都符合条件,那么快慢指针将一直相等地移动下去,直到最后输出数组的长度。
如果出现三次重复的,则只移动快指针。在纸上比划一下就可以理解。

26题删除有序数组中的重复项 仍可使用这道题的代码。将2全部改为1即可,道理一样
思路在注释中,第一行的判断不写也可以通过,因为用例里没有长度小于3的数组。

代码

/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function(nums) {
    // if (nums.length <= 2) return nums.length;
    // s为slow指针 f为fast指针
    let s = 2;//既代表索引(慢指针) 也代表最后记录的长度
    // 以1开始 是因为 nums一定有值 经过前端筛选之后 最后输出长度最少是2
    for (let f = 2; f < nums.length; f++) {//循环索引相当于快指针
        if (nums[s - 2] !== nums[f]) {
            nums[s] = nums[f];//原地修改
            s++;//输出长度+1
        }
        // 相等的时候只移动快指针,即循环的索引
    }
    return s;
};