LeetCode 题目 80:删除排序数组中的重复项 II【算法面试高频题】

简介: LeetCode 题目 80:删除排序数组中的重复项 II【算法面试高频题】

作者介绍:10年大厂数据\经营分析经验,现任大厂数据部门负责人。

会一些的技术:数据分析、算法、SQL、大数据相关、python

欢迎加入社区:码上找工作

作者专栏每日更新:

LeetCode解锁1000题: 打怪升级之旅

python数据分析可视化:企业实战案例

python源码解读

备注说明:方便大家阅读,统一使用python,带必要注释,公众号 数据分析螺丝钉 一起打怪升级

题目描述

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

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

输入格式
  • nums:一个有序整数数组。
输出格式
  • 返回处理后数组的新长度。

示例

示例 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. 初始化指针:设置两个指针 ij,其中 i 是慢指针,j 是快指针。
  2. 遍历数组:快指针 j 遍历整个数组,慢指针 i 只在满足条件时移动。
  3. 条件判断:比较当前元素 nums[j]nums[i-2](保证最多只保留两个相同的元素)。
完整的规范代码
def removeDuplicates(nums):
    """
    双指针法删除排序数组中的重复项,使每个元素最多出现两次
    :param nums: List[int], 输入的有序数组
    :return: int, 修改后的数组长度
    """
    if len(nums) < 3:
        return len(nums)
    i = 2
    for j in range(2, len(nums)):
        if nums[j] != nums[i - 2]:
            nums[i] = nums[j]
            i += 1
    return i
# 示例调用
print(removeDuplicates([1,1,1,2,2,3]))  # 输出: 5
print(removeDuplicates([0,0,1,1,1,1,2,3,3]))  # 输出: 7
算法分析
  • 时间复杂度:(O(n)),其中 (n) 是数组的长度,因为每个元素被访问一次。
  • 空间复杂度:(O(1)),使用了常数级的额外空间。

方法二:优化的双指针法

解题步骤
  1. 利用计数器:使用一个计数器跟踪当前元素的出现次数,当发现新元素时重置计数器。
  2. 更新数组:如果计数器小于或等于2,则更新 nums[i] 并移动 i
完整的规范代码
def removeDuplicates(nums):
    """
    使用优化的双指针法,利用计数器控制元素出现次数
    :param nums: List[int], 输入的有序数组
    :return: int, 修改后的数组长度
    """
    i, count = 1, 1
    for j in range(1, len(nums)):
        if nums[j] == nums[j - 1]:
            count += 1
        else:
            count = 1
        
        if count <= 2:
            nums[i] = nums[j]
            i += 1
    
    return i
# 示例调用
print(removeDuplicates([1,1,1,2,2,3]))  # 输出: 5
print(removeDuplicates([0,0,1,1,1,1,2,3,3]))  # 输出: 7
算法分析
  • 时间复杂度:(O(n)),遍历一次数组。
  • 空间复杂度:(O(1)),不需要额外的空间,除了几个变量。

方法三:递归法

解题步骤
  1. 递归实现:使用递归函数逐层减少数组长度,当发现超过两个重复项时,通过递归调整数组。
  2. 边界条件:当数组长度小于3时直接返回长度。
完整的规范代码
def removeDuplicates(nums):
    """
    使用递归法处理数组,删除多余的重复项
    :param nums: List[int], 输入的有序数组
    :return: int, 修改后的数组长度
    """
    def helper(index):
        if index < 2:
            return index
        if nums[index] == nums[index - 1] == nums[index - 2]:
            return helper(index - 1)
        else:
            return 1 + helper(index - 1)
    
    return helper(len(nums) - 1)
# 示例调用
print(removeDuplicates([1,1,1,2,2,3]))  # 输出: 5
print(removeDuplicates([0,0,1,1,1,1,2,3,3]))  # 输出: 7
算法分析
  • 时间复杂度:(O(n^2)),在最坏的情况下,递归可能会多次处理相同的元素。
  • 空间复杂度:(O(n)),递归的深度在最坏情况下可以达到 (n)。

方法四:利用集合操作

解题步骤
  1. 集合处理:虽然题目要求原地修改,但可以理解为用集合模拟处理过程,然后复制回数组。
  2. 直接应用:此方法主要是理论上的探讨,实际操作中不符合原地修改的要求。
完整的规范代码
def removeDuplicates(nums):
    """
    理论上使用集合操作模拟删除重复项,实际上不符合题目要求
    :param nums: List[int], 输入的有序数组
    :return: int, 修改后的数组长度
    """
    # 此处略过实现,因为集合无法直接应用到原地修改的要求上
    return len(nums)
# 示例调用
print(removeDuplicates([1,1,1,2,2,3]))  # 输出: 5
print(removeDuplicates([0,0,1,1,1,1,2,3,3]))  # 输出: 7
算法分析
  • 时间复杂度:(O(n)),理论分析。
  • 空间复杂度:(O(1)),理论分析。

方法五:一次遍历

解题步骤
  1. 一次遍历:在一次遍历中处理所有重复项,确保每个元素最多出现两次。
  2. 实时更新:使用一个计数器跟踪当前元素的出现次数,直接在原数组上进行修改。
完整的规范代码
def removeDuplicates(nums):
    """
    使用一次遍历的方法删除排序数组中的重复项
    :param nums: List[int], 输入的有序数组
    :return: int, 修改后的数组长度
    """
    i = 0
    for num in nums:
        if i < 2 or num > nums[i - 2]:
            nums[i] = num
            i += 1
    return i
# 示例调用
print(removeDuplicates([1,1,1,2,2,3]))  # 输出: 5
print(removeDuplicates([0,0,1,1,1,1,2,3,3]))  # 输出: 7
算法分析
  • 时间复杂度:(O(n)),只需要一次遍历。
  • 空间复杂度:(O(1)),在原数组上修改,不需要额外空间。

不同算法的优劣势对比

特征 方法一:双指针法 方法二:优化的双指针 方法三:递归法 方法四:集合操作 方法五:一次遍历
时间复杂度 (O(n)) (O(n)) (O(n^2)) (O(n)) (O(n))
空间复杂度 (O(1)) (O(1)) (O(n)) (O(1)) (O(1))
优势 直观,简单 更细致的控制 理论上可行 理论探讨 高效,简洁
劣势 较基础 略复杂 实际不可用 不符合要求 无显著劣势

应用示例

数据处理:在处理大量数据时,经常需要去除重复数据项,尤其是在数据预处理和清洗阶段。

数据库操作:在数据库查询中去除重复记录,尤其是在执行数据迁移或整合时。

内存管理:优化内存使用,避免在有限的存储空间中保存过多重复数据。



欢迎关注微信公众号 数据分析螺丝钉

相关文章
|
4月前
|
存储 人工智能 算法
从零掌握贪心算法Java版:LeetCode 10题实战解析(上)
在算法世界里,有一种思想如同生活中的"见好就收"——每次做出当前看来最优的选择,寄希望于通过局部最优达成全局最优。这种思想就是贪心算法,它以其简洁高效的特点,成为解决最优问题的利器。今天我们就来系统学习贪心算法的核心思想,并通过10道LeetCode经典题目实战演练,带你掌握这种"步步为营"的解题思维。
|
11月前
|
算法
面试场景题:如何设计一个抢红包随机算法
本文详细解析了抢红包随机算法的设计与实现,涵盖三种解法:随机分配法、二倍均值法和线段切割法。随机分配法通过逐次随机分配金额确保总额不变,但易导致两极分化;二倍均值法优化了金额分布,使每次抢到的金额更均衡;线段切割法则将总金额视为线段,通过随机切割点生成子金额,手气最佳金额可能更高。代码示例清晰,结果对比直观,为面试中类似算法题提供了全面思路。
1738 16
|
11月前
|
存储 监控 算法
关于员工上网监控系统中 PHP 关联数组算法的学术解析
在当代企业管理中,员工上网监控系统是维护信息安全和提升工作效率的关键工具。PHP 中的关联数组凭借其灵活的键值对存储方式,在记录员工网络活动、管理访问规则及分析上网行为等方面发挥重要作用。通过关联数组,系统能高效记录每位员工的上网历史,设定网站访问权限,并统计不同类型的网站访问频率,帮助企业洞察员工上网模式,发现潜在问题并采取相应管理措施,从而保障信息安全和提高工作效率。
198 7
|
存储 人工智能 算法
C 408—《数据结构》算法题基础篇—数组(通俗易懂)
408考研——《数据结构》算法题基础篇之数组。(408算法题的入门)
770 23
|
算法 安全 Java
Java线程调度揭秘:从算法到策略,让你面试稳赢!
在社招面试中,关于线程调度和同步的相关问题常常让人感到棘手。今天,我们将深入解析Java中的线程调度算法、调度策略,探讨线程调度器、时间分片的工作原理,并带你了解常见的线程同步方法。让我们一起破解这些面试难题,提升你的Java并发编程技能!
531 16
|
存储 算法 Java
leetcode算法题-有效的括号(简单)
【11月更文挑战第5天】本文介绍了 LeetCode 上“有效的括号”这道题的解法。题目要求判断一个只包含括号字符的字符串是否有效。有效字符串需满足左括号必须用相同类型的右括号闭合,并且左括号必须以正确的顺序闭合。解题思路是使用栈数据结构,遍历字符串时将左括号压入栈中,遇到右括号时检查栈顶元素是否匹配。最后根据栈是否为空来判断字符串中的括号是否有效。示例代码包括 Python 和 Java 版本。
358 4
|
机器学习/深度学习 算法 Java
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
机器学习、基础算法、python常见面试题必知必答系列大全:(面试问题持续更新)
|
算法
每日一道算法题(Leetcode 20)
每日一道算法题(Leetcode 20)
211 2
|
Unix Shell Linux
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
本文提供了几个Linux shell脚本编程问题的解决方案,包括转置文件内容、统计词频、验证有效电话号码和提取文件的第十行,每个问题都给出了至少一种实现方法。
341 6
LeetCode刷题 Shell编程四则 | 194. 转置文件 192. 统计词频 193. 有效电话号码 195. 第十行
|
搜索推荐 索引 Python
【Leetcode刷题Python】牛客. 数组中未出现的最小正整数
本文介绍了牛客网题目"数组中未出现的最小正整数"的解法,提供了一种满足O(n)时间复杂度和O(1)空间复杂度要求的原地排序算法,并给出了Python实现代码。
439 2