LeetCode 80.删除有序数组中的重复项:快慢指针+间距审核法

0 阅读4分钟

前言

相比前面两天的题目稍微难一点,我将在本文中梳理我的思考过程 审题 画图 每一步转换 找特殊情况 打代码


一、题目再解读

原题题干

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

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

O(1):不随n的增加而增加即可 可以有1、2、C个固定的变量 不单单只是一个

核心规则(抓重点)

原地修改:不能新建数组,只能在原数组上改,空间复杂度O(1)​

返回值:返回移除后的数组长度k,原数组前k个元素为有效元素​

顺序有关:有效元素顺序不可以打乱。

无需处理k之后的元素,系统不会校验​ 示例演示

示例 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。不需要考虑数组中超出新长度后面的元素。

二、新手探索实录(我的思考复盘)

第一次写这道题时,我想用快慢双指针思路,但总觉得缺一个变量stock (判断一个数字是否已经出现两次了),但我稍微卡住了,靠想想不出来。

然后我在纸上画出 第一个实例 的变化状态

S是我们的慢指针 F是我们的快指针 sea也是一个指针 它像东海龙王一样 坐落与最新见到的数字的第一个位置

每次F都会往前一格,它就像警察一样审核它指向的犯人

我的循环默认情况是nums[F]和nums[S]相同,所以F自动增一,其他都是特殊情况

而我的特殊情况就是在以下状态转换图里找的了

6f6033e143190cb1a3056911773cbc8.jpg


ex: 状态1 nums[F]===nums[S] 但是 F-sea==1(数组[1,1]符合条件 ):

slow多走一步

F++

状态2 nums[F]===nums[S] 但是 F-sea==2(数组[1,1,1]不符合条件 ):

slow不走了 让fast警察自己去抓小偷 所以这个可以归为默认情况

F++

状态3 遇见新数字了:

先让sea改变位置先

再让S走一步

这个时候就可以就行赋值了 nums[S]=nums[F]

状态4 nums[F]===nums[S] 但是 F-sea==1(数组[1,1,2,2]符合条件 ):

slow多走一步

状态5 又遇见新数字了:

先让sea改变位置先

再让S走一步

这个时候就可以就行赋值了 nums[S]=nums[F]

以下是我的代码


/**
 * @param {number[]} nums
 * @return {number}
 */
var removeDuplicates = function (nums) {
    let len = nums.length;
    if (len === 0) {
        return 0;
    }
    //创建三个变量-->用他们之间的距离来查即可
    let slow = 0;
    let sea = 0;
    for (let fast = 1; fast < len; fast++) {
        //判断遇到新的数字再改变sea(第一旧重复数字的位置)
        if (nums[sea] != nums[fast]) {
            sea=fast;
        }

        if ((nums[fast] === nums[slow]) && (fast - sea == 1)) {
            slow++;
            nums[slow]=nums[fast];
        } else if (nums[fast] != nums[slow]) {
            slow++;
            nums[slow]=nums[fast];

        }
    }
    return slow + 1;
};

其中还有一些巧妙的地方我不知道怎么的,突然在脑子里蹦出来的,不知道怎么说,还请见谅

三、最优解法:同向快慢指针(O (n) 时间 + O (1) 空间)

解法思路

数组是有序的 → 重复数字一定挨在一起!

两个指针

  1. fast 快指针往前遍历,一个个检查元素
  2. slow 慢指针记录新数组位置,存放合法的元素
var removeDuplicates = function(nums) { 
    let slow = 2; 
    for (let fast = 2; fast < nums.length; fast++) { 
        if (nums[fast] !== nums[slow - 2]) { nums[slow++] = nums[fast]; 
        } 
    }
    return Math.min(slow, nums.length); 
};

四、复杂度分析

时间复杂度:O(n),数组仅遍历一次,双指针移动总次数不超过n

空间复杂度:O(1),仅用了两个指针变量,无额外数组空间开销,完全符合题目要求


结语

还得继续学习,解法不够优秀,思考太久,写博客也写了很久,以后写快点 加油加油