4482 字
约 14 分钟
10
080.删除有序数组中的重复项 II
无标签

080.删除有序数组中的重复项 II

作者:沉默王二
发布时间:2023-09-24T08:14:53.489+0800


本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

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

难度

中等

示例

示例 1:

输入:nums = [1,1,1,2,2,3]

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

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]

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

分析

这道题很容易让我们联想到 026.删除有序数组中的重复项 ,当时我们使用的是暴力算法,先解出答案再说,因为 026.删除有序数组中的重复项 的条件限制比较严格,很难直接采用双指针的算法,今天这道题,我们打算试试双指针。

  • 双指针算法使用两个指针在数据结构中遍历,如数组或链表。这种方法可以有效减少时间和空间复杂度。
  • 暴力算法,也被称为朴素算法,是解决问题的一种简单直接的方法。其基本思路是先把题解出来再说,不考虑时间和空间复杂度。

首先我们来观察数组长度较小的情况下,我们应该如何解题。

如果nums.length <= 2,那么我们就不需要考虑如何处理,直接返回原数组的长度即可,因为如果存在重复项,最多也只可能出现两次。

如果nums.length == 3,我们只需要判断nums[2]和nums[0]是否相等即可,因为二者如果相等,按照有序数组这一特性,我们可以确定nums[0],nums[1],nums[2]就是相等的元素,直接除掉nums[2]返回就行了。

推算到到这一步,我们就可以将整个nums分为已检查区、检查区/排除区和未检查区来套用上面的策略。

在上述例子中,nums.length 小于 2 的情况下,我们均不用对里面的元素进行检查,或者说,已经利用规则将其自动检查了,当nums.length 等于 3 的情况下,我们也不用对nums[0],nums[1]进行检查,只需要排除第 3 个元素即可。

其实到这一步,我们的算法雏形已经浮现出来了,采取两个指针lef,rig,开始都指向2的位置,然后我们把整个nums[]分割成已检查区 和 未检查区,[0-(lef-1)]的区域为已检查区,[rig-(n-1)]为未检查区。

接下来,我们的任务就是保证已检查区内的元素个数不能超过 2。

我们向后移动rig,并且对这时的nums[rig]进行检查,如果这时候nums[rig] == nums[lef - 2],我们就必须舍去nums[rig],因为如果nums[rig] == nums[lef - 2],根据数列有序的这一特性,则nums[lef - 1] == nums[lef - 2],这样子就必然会有三个一样的数字出现,所以我们舍去nums[rig],向后移动rig。

那如果nums[rig] != nums[lef - 2]呢?

这时候我们就要将这个新的元素纳入已检查区了,因为这表示已检查区最多也就只有一个跟nums[rig]相等的元素,所以我们就可以大胆地将其纳入已检查区。

最后的答案,自然就是已检查区的长度。

具体实现:

class Solution {

        // 如果数组长度小于等于2,则直接返回长度        if(nums.length <= 2)            return nums.length;
        // checked 表示当前检查到的位置,也是下一个要放入的位置        // checking 表示当前正在检查的位置        int checked = 2, checking = 2;
        // 遍历数组        while(checking < nums.length){            // 如果当前检查的数字和 checked 位置的前两个数字不同,            // 这意味着我们可以把这个数字放到 checked 位置            if(nums[checked - 2] != nums[checking]){                nums[checked] = nums[checking];                checked++;            }            // 无论是否复制数字,都向前移动 checking 指针            checking++;        }
        // 返回新的数组长度        return checked;    }}

OK,来看解题效率:

我们来模拟一下当前的解题过程。

题目的目标是删除有序数组中的重复项,但是每个元素最多可以出现两次。为了解决这个问题,我们使用两个指针来遍历数组。

  • 指针checked:指向应当被修改的位置。
  • 指针checking:遍历数组,检查每个元素是否应当被复制到checked位置。

基本思路:

当 nums[checked - 2] != nums[checking] 时,表示当前checking所指向的元素与checked前两个元素不同,因此可以被复制。

图解:

假设我们的输入数组为:

nums = [1,1,1,2,2,3,3]

初始状态:

nums:      1  1  1  2  2  3  3

pointers:  c  c

(此处 c 代表 checked 和 checking 都在此位置)

第一步:nums[checked - 2] 与 nums[checking] 相同,因此我们只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c     c

第二步:现在 nums[checked - 2] 与 nums[checking] 仍然相同,只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c        c

第三步:现在 nums[checked - 2] 与 nums[checking] 不同,复制元素:

nums:      1  1  2  2  2  3  3

pointers:  c           c

按照这样的方式继续,直到checking到达数组末尾。

最终:

nums:      1  1  2  2  3  3  3

pointers:                  c  c

返回值为checked的值:5,表示有效的元素为[1,1,2,2,3]。

总结

本题主要还是双指针算法的运用,利用双指针法可以巧妙的将整个nums分开,从而保证已检查区内的每个元素的数量不超过2。

力扣链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/

本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

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

难度

中等

示例

示例 1:

输入:nums = [1,1,1,2,2,3]

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

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]

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

分析

这道题很容易让我们联想到 026.删除有序数组中的重复项 ,当时我们使用的是暴力算法,先解出答案再说,因为 026.删除有序数组中的重复项 的条件限制比较严格,很难直接采用双指针的算法,今天这道题,我们打算试试双指针。

  • 双指针算法使用两个指针在数据结构中遍历,如数组或链表。这种方法可以有效减少时间和空间复杂度。
  • 暴力算法,也被称为朴素算法,是解决问题的一种简单直接的方法。其基本思路是先把题解出来再说,不考虑时间和空间复杂度。

首先我们来观察数组长度较小的情况下,我们应该如何解题。

如果nums.length <= 2,那么我们就不需要考虑如何处理,直接返回原数组的长度即可,因为如果存在重复项,最多也只可能出现两次。

如果nums.length == 3,我们只需要判断nums[2]和nums[0]是否相等即可,因为二者如果相等,按照有序数组这一特性,我们可以确定nums[0],nums[1],nums[2]就是相等的元素,直接除掉nums[2]返回就行了。

推算到到这一步,我们就可以将整个nums分为已检查区、检查区/排除区和未检查区来套用上面的策略。

在上述例子中,nums.length 小于 2 的情况下,我们均不用对里面的元素进行检查,或者说,已经利用规则将其自动检查了,当nums.length 等于 3 的情况下,我们也不用对nums[0],nums[1]进行检查,只需要排除第 3 个元素即可。

其实到这一步,我们的算法雏形已经浮现出来了,采取两个指针lef,rig,开始都指向2的位置,然后我们把整个nums[]分割成已检查区 和 未检查区,[0-(lef-1)]的区域为已检查区,[rig-(n-1)]为未检查区。

接下来,我们的任务就是保证已检查区内的元素个数不能超过 2。

我们向后移动rig,并且对这时的nums[rig]进行检查,如果这时候nums[rig] == nums[lef - 2],我们就必须舍去nums[rig],因为如果nums[rig] == nums[lef - 2],根据数列有序的这一特性,则nums[lef - 1] == nums[lef - 2],这样子就必然会有三个一样的数字出现,所以我们舍去nums[rig],向后移动rig。

那如果nums[rig] != nums[lef - 2]呢?

这时候我们就要将这个新的元素纳入已检查区了,因为这表示已检查区最多也就只有一个跟nums[rig]相等的元素,所以我们就可以大胆地将其纳入已检查区。

最后的答案,自然就是已检查区的长度。

具体实现:

class Solution {

        // 如果数组长度小于等于2,则直接返回长度        if(nums.length <= 2)            return nums.length;
        // checked 表示当前检查到的位置,也是下一个要放入的位置        // checking 表示当前正在检查的位置        int checked = 2, checking = 2;
        // 遍历数组        while(checking < nums.length){            // 如果当前检查的数字和 checked 位置的前两个数字不同,            // 这意味着我们可以把这个数字放到 checked 位置            if(nums[checked - 2] != nums[checking]){                nums[checked] = nums[checking];                checked++;            }            // 无论是否复制数字,都向前移动 checking 指针            checking++;        }
        // 返回新的数组长度        return checked;    }}

OK,来看解题效率:

我们来模拟一下当前的解题过程。

题目的目标是删除有序数组中的重复项,但是每个元素最多可以出现两次。为了解决这个问题,我们使用两个指针来遍历数组。

  • 指针checked:指向应当被修改的位置。
  • 指针checking:遍历数组,检查每个元素是否应当被复制到checked位置。

基本思路:

当 nums[checked - 2] != nums[checking] 时,表示当前checking所指向的元素与checked前两个元素不同,因此可以被复制。

图解:

假设我们的输入数组为:

nums = [1,1,1,2,2,3,3]

初始状态:

nums:      1  1  1  2  2  3  3

pointers:  c  c

(此处 c 代表 checked 和 checking 都在此位置)

第一步:nums[checked - 2] 与 nums[checking] 相同,因此我们只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c     c

第二步:现在 nums[checked - 2] 与 nums[checking] 仍然相同,只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c        c

第三步:现在 nums[checked - 2] 与 nums[checking] 不同,复制元素:

nums:      1  1  2  2  2  3  3

pointers:  c           c

按照这样的方式继续,直到checking到达数组末尾。

最终:

nums:      1  1  2  2  3  3  3

pointers:                  c  c

返回值为checked的值:5,表示有效的元素为[1,1,2,2,3]。

总结

本题主要还是双指针算法的运用,利用双指针法可以巧妙的将整个nums分开,从而保证已检查区内的每个元素的数量不超过2。

力扣链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/

本题解同时收录在星球专栏《二哥的 LeetCode 刷题笔记》中,专栏托管在语雀平台上(更方便你沉浸式阅读和做笔记),访问地址和密码:https://t.zsxq.com/6iuzn6I

题意

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

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

难度

中等

示例

示例 1:

输入:nums = [1,1,1,2,2,3]

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

示例 2:

输入:nums = [0,0,1,1,1,1,2,3,3]

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

分析

这道题很容易让我们联想到 026.删除有序数组中的重复项 ,当时我们使用的是暴力算法,先解出答案再说,因为 026.删除有序数组中的重复项 的条件限制比较严格,很难直接采用双指针的算法,今天这道题,我们打算试试双指针。

  • 双指针算法使用两个指针在数据结构中遍历,如数组或链表。这种方法可以有效减少时间和空间复杂度。
  • 暴力算法,也被称为朴素算法,是解决问题的一种简单直接的方法。其基本思路是先把题解出来再说,不考虑时间和空间复杂度。

首先我们来观察数组长度较小的情况下,我们应该如何解题。

如果nums.length <= 2,那么我们就不需要考虑如何处理,直接返回原数组的长度即可,因为如果存在重复项,最多也只可能出现两次。

如果nums.length == 3,我们只需要判断nums[2]和nums[0]是否相等即可,因为二者如果相等,按照有序数组这一特性,我们可以确定nums[0],nums[1],nums[2]就是相等的元素,直接除掉nums[2]返回就行了。

推算到到这一步,我们就可以将整个nums分为已检查区、检查区/排除区和未检查区来套用上面的策略。

在上述例子中,nums.length 小于 2 的情况下,我们均不用对里面的元素进行检查,或者说,已经利用规则将其自动检查了,当nums.length 等于 3 的情况下,我们也不用对nums[0],nums[1]进行检查,只需要排除第 3 个元素即可。

其实到这一步,我们的算法雏形已经浮现出来了,采取两个指针lef,rig,开始都指向2的位置,然后我们把整个nums[]分割成已检查区 和 未检查区,[0-(lef-1)]的区域为已检查区,[rig-(n-1)]为未检查区。

接下来,我们的任务就是保证已检查区内的元素个数不能超过 2。

我们向后移动rig,并且对这时的nums[rig]进行检查,如果这时候nums[rig] == nums[lef - 2],我们就必须舍去nums[rig],因为如果nums[rig] == nums[lef - 2],根据数列有序的这一特性,则nums[lef - 1] == nums[lef - 2],这样子就必然会有三个一样的数字出现,所以我们舍去nums[rig],向后移动rig。

那如果nums[rig] != nums[lef - 2]呢?

这时候我们就要将这个新的元素纳入已检查区了,因为这表示已检查区最多也就只有一个跟nums[rig]相等的元素,所以我们就可以大胆地将其纳入已检查区。

最后的答案,自然就是已检查区的长度。

具体实现:

class Solution {

        // 如果数组长度小于等于2,则直接返回长度        if(nums.length <= 2)            return nums.length;
        // checked 表示当前检查到的位置,也是下一个要放入的位置        // checking 表示当前正在检查的位置        int checked = 2, checking = 2;
        // 遍历数组        while(checking < nums.length){            // 如果当前检查的数字和 checked 位置的前两个数字不同,            // 这意味着我们可以把这个数字放到 checked 位置            if(nums[checked - 2] != nums[checking]){                nums[checked] = nums[checking];                checked++;            }            // 无论是否复制数字,都向前移动 checking 指针            checking++;        }
        // 返回新的数组长度        return checked;    }}

OK,来看解题效率:

我们来模拟一下当前的解题过程。

题目的目标是删除有序数组中的重复项,但是每个元素最多可以出现两次。为了解决这个问题,我们使用两个指针来遍历数组。

  • 指针checked:指向应当被修改的位置。
  • 指针checking:遍历数组,检查每个元素是否应当被复制到checked位置。

基本思路:

当 nums[checked - 2] != nums[checking] 时,表示当前checking所指向的元素与checked前两个元素不同,因此可以被复制。

图解:

假设我们的输入数组为:

nums = [1,1,1,2,2,3,3]

初始状态:

nums:      1  1  1  2  2  3  3

pointers:  c  c

(此处 c 代表 checked 和 checking 都在此位置)

第一步:nums[checked - 2] 与 nums[checking] 相同,因此我们只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c     c

第二步:现在 nums[checked - 2] 与 nums[checking] 仍然相同,只移动checking:

nums:      1  1  1  2  2  3  3

pointers:  c        c

第三步:现在 nums[checked - 2] 与 nums[checking] 不同,复制元素:

nums:      1  1  2  2  2  3  3

pointers:  c           c

按照这样的方式继续,直到checking到达数组末尾。

最终:

nums:      1  1  2  2  3  3  3

pointers:                  c  c

返回值为checked的值:5,表示有效的元素为[1,1,2,2,3]。

总结

本题主要还是双指针算法的运用,利用双指针法可以巧妙的将整个nums分开,从而保证已检查区内的每个元素的数量不超过2。

力扣链接:https://leetcode.cn/problems/remove-duplicates-from-sorted-array-ii/

            知识星球
            
    
    
        
        扫码加入星球
        查看更多优质内容
    
    https://wx.zsxq.com/mweb/views/joingroup/join_group.html?group_id=15522885221412
080.删除有序数组中的重复项 II
https://clxhxhhr.top/posts/2534/
作者
clxstart
发布于
2026-09-19
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。