代码训练LeetCode(7)删除有序数组中的重复项

代码训练(7)LeetCode之删除有序数组中的重复项

Author: Once Day Date: 2024年3月10日

漫漫长路,才刚刚开始…

全系列文章可参考专栏: 十年代码训练_Once-Day的博客-CSDN博客

参考文章:

  • 26. 删除有序数组中的重复项 - 力扣(LeetCode)
  • 力扣 (LeetCode) 全球极客挚爱的技术成长平台

文章目录

      • 代码训练(7)LeetCode之删除有序数组中的重复项
        • 1. 原题
        • 2. 分析
        • 3. 代码实现
          • 3.1 基础实现
          • 3.2 过度优化版本代码
        • 4. 总结

1. 原题

给你一个 非严格递增排列 的数组 nums ,请你原地删除重复出现的元素,使每个元素 只出现一次 ,返回删除后数组的新长度。元素的 相对顺序 应该保持 一致 。然后返回 nums 中唯一元素的个数。

考虑 nums 的唯一元素的数量为 k ,你需要做以下事情确保你的题解可以被通过:

  • 更改数组 nums ,使 nums 的前 k 个元素包含唯一元素,并按照它们最初在 nums 中出现的顺序排列。nums 的其余元素与 nums 的大小不重要。
  • 返回 k

例如,对于nums = [1, 2, 2, 3],其输出长度为3,输出数据为nums = [1, 2, 3, x]即可。

2. 分析

有一个数组 nums,它是非严格递增的,也就是说,数组中的元素可能会有重复,并且它们是按照顺序排列的。你的任务是要在不改变元素相对顺序的情况下,仅保留每个元素的一个副本,并删除所有的重复元素。你需要在不使用额外数组的条件下完成这个操作,这就意味着你需要在原地修改输入的数组 nums

解题思路:

  1. 保持两个指针,i 作为慢指针,j 作为快指针。
  2. 遍历数组,快指针 j 用于寻找下一个与慢指针 i 指向的数字不同的数字。
  3. 当快指针 j 找到一个新的不同数字时,将慢指针 i 向前移动一位,然后将 j 指向的数字复制到 i 的位置。
  4. 重复步骤2和3,直到快指针 j 遍历完整个数组。

分析步骤:

  • 初始化两个指针 ij,其中 i = 0j = 1
  • j小于数组nums的长度时,执行以下操作:
    • 如果 nums[j]nums[i] 相同,j++,跳过重复的元素。
    • 如果 nums[j]nums[i] 不同,i++,并将 nums[j] 的值赋给 nums[i]
  • 继续执行上述步骤直到 j 遍历完数组。
  • 最终,i+1 就是数组中不重复元素的个数。

举例分析:
如果输入的数组 nums[1, 1, 2],初始时 i=0j=1

  • 因为 nums[j] 等于 nums[i],所以 j++,现在 j=2
  • 现在 nums[j] 不等于 nums[i],所以 i++,将 nums[j] 的值赋给 nums[i],现在 nums 变为 [1, 2, 2]
  • j 继续递增,因为已经到数组末尾,停止循环。
  • 最终 i+1 等于 2,表示数组 nums 中有两个不重复的元素。

性能优化关键点:

  • 尽量减少不必要的操作,例如在快指针找到新元素时才移动慢指针。
  • 避免使用额外的内存空间,直接在原数组上操作。
3. 代码实现
3.1 基础实现
int removeDuplicates(int* nums, int numsSize) {if (numsSize == 0) return 0;int i = 0;for (int j = 1; j < numsSize; j++) {if (nums[j] != nums[i]) {i++;nums[i] = nums[j];}}return i + 1;
}

这段代码含义如下:

  • removeDuplicates 函数接受一个整数数组 nums和它的大小 numsSize,然后返回一个整数表示唯一元素的数量。
  • main 函数中,我们定义了一个示例数组并调用 removeDuplicates 函数。
  • 然后打印出修改后的数组和唯一元素的数量。

运行测试结果如下(仅供参考):

在这里插入图片描述

这段代码性能不算非常高,因为存在nums[i]这种随机访问,并且判断nums[j] != nums[i]在大部分情况也有些多余。

3.2 过度优化版本代码

注意,本部分代码仅供各位C语言爱好者一乐,真实项目别写这种风格代码,上面3.1简单版本的代码更合适

struct temp{int last;int curr;
};int removeDuplicates(int* nums, int numsSize) {int *deal_nums, *resv_nums, *max_nums;#define unlikely(x) __builtin_expect(!!(x), 0)
#define likely(x) __builtin_expect(!!(x), 1)if (unlikely(numsSize <= 1)) {return numsSize;}max_nums  = &nums[numsSize] - 1;deal_nums = nums;resv_nums = &nums[1];while(likely(deal_nums < max_nums)) {struct temp *temp = (struct temp *)deal_nums++;if (temp->curr ^ temp->last) {if (deal_nums != resv_nums) {*resv_nums = temp->curr;goto another;}resv_nums++;} }while(likely(deal_nums < max_nums)) {struct temp *temp = (struct temp *)deal_nums++;if (temp->curr ^ temp->last) {*resv_nums = temp->curr;
another:resv_nums++;} }return resv_nums - nums;
}

代码中使用了一个结构体 temp,包含两个整型成员 lastcurr,用于同时存储相邻的两个元素。

主要的优化点如下:

  1. 使用 unlikelylikely 宏来优化分支预测,提高代码执行效率。
  2. 使用指针操作来遍历数组,避免了数组下标的计算,提高了性能。
  3. 使用异或运算 ^ 来比较相邻元素是否相等,避免了条件分支的使用,提高了性能。
  4. 使用 goto 语句来优化代码流程,减少了重复的代码。

缺点也很明显:

  • 代码可读性较差,使用了一些技巧性的优化,如位运算和 goto 语句,可能会影响代码的可维护性。
  • 虽然使用了一些优化手段,但整体的时间复杂度仍然是 O(n),与普通的去重算法相比,优化的效果可能并不明显。

这段代码在一定程度上提高了去重操作的性能,但也牺牲了一定的可读性和可维护性。

实际运行结果如下(仅供参考):

在这里插入图片描述

4. 总结

这个题目考查了对数组操作的基本能力,特别是双指针技巧,这是一个常用于解决类似问题的方法。为了提高编程能力,应当熟练掌握数组相关的操作,包括但不限于遍历、插入、删除等。

同时,熟悉双指针技巧在其他问题中的应用也非常重要,即如何在不使用额外空间的情况下修改输入数据







Alt

Once Day

也信美人终作土,不堪幽梦太匆匆......

如果这篇文章为您带来了帮助或启发,不妨点个赞👍和关注,再加上一个小小的收藏⭐!

(。◕‿◕。)感谢您的阅读与支持~~~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/735177.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c++ 类内可以定义引用数据成员吗?

在C中&#xff0c;类内是可以定义引用数据成员的&#xff0c;但是在初始化对象时&#xff0c;必须在构造函数的成员初始化列表中对引用进行初始化&#xff0c;因为引用必须在创建时被初始化&#xff0c;并且不能在其生存期内引用不同的对象。下面是一个简单的示例&#xff1a; …

【病毒查杀】“PE启动盘+360杀毒软件”对硬盘病毒查杀

通过本步骤&#xff0c;用户可使用WinPE版本360系统急救箱软件对主板硬盘进行病毒木马扫描和杀毒操作。 360系统急救箱&#xff0c;这款软件是360官方推出的一款杀毒工具箱&#xff0c;可以帮助用户仅在WinPE环境下就可对硬盘进行病毒木马查杀&#xff0c;可能查杀过后因病毒木…

网络安全: Kali Linux 进行 MSFvenom 程序利用

目录 一、实验 1.环境 2. Kali Linux 进行 MSFvenom 程序利用 3. 创建计划任务自动运行 MSFvenom 程序 二、问题 1.在线加密解密 2.MSF 运行失败 3.MobaXterm 连接Ubuntu 失败 一、实验 1.环境 &#xff08;1&#xff09;主机 表1 主机 系统版本IP备注Kali Linux20…

STM32单片机基本原理与应用(十一)

语音识别实验 此实验采用STM32核心板 LD3320模块&#xff0c;通过初始化LD3320并写入待识别关键词&#xff0c;对麦克风说出相应关键词&#xff0c;实现实训平台上的流水灯相应变化的效果。 LD3320 是一颗基于非特定人语音识别 &#xff08;SI-ASR&#xff1a;Speaker-Indepen…

Python基础知识:数字类型及数学函数详解

在开启Python系列学习之后&#xff0c;我们就大概讲了数字类型及数学函数的基础语法。这一篇博客&#xff0c;我们就将详细介绍Python中的数字类型、数学函数、随机数函数、三角函数以及数学常量&#xff0c;同时&#xff0c;为大家提供一些示例代码帮助大家更好地理解它们的用…

C#拾遗补漏之goto跳转语句

前言 在我们日常工作中常用的C#跳转语句有break、continue、return&#xff0c;但是还有一个C#跳转语句很多同学可能都比较的陌生就是goto&#xff0c;今天大姚带大家一起来认识一下goto语句及其它的优缺点。 goto语句介绍 goto 语句由关键字 goto 后跟一个标签名称组成&…

视频产品介绍:AS-VCVR-N多协议视频接入网关

目 录 一、产品概述 &#xff08;一&#xff09;非标设备接入 &#xff08;二&#xff09;信令流转换 &#xff08;三&#xff09;媒体流转发 二、网关特性 三、技术参数 一、产品概述 视频接入网关服务是终端用户与视频源的传输枢纽&#xff0c;实现把前端不同…

AI训练数据处理和读取

AI训练数据处理和读取 AI数据处理 AI数据处理概述 AI数据处理是指对原始数据进行一系列的操作&#xff0c;以便机器学习算法能够从中提取有用的信息&#xff0c;进而进行模型的训练、验证和部署。数据处理是机器学习流程中的关键步骤&#xff0c;其质量直接影响到模型的性能…

手把手一起开发SV4E-I3C设备(四)

JEDEC DDR5 SPD Hub Devices例程 所有例程&#xff0c;SV4E-I3C设备连接器件均为SPD5芯片&#xff0c;Reg表示MR寄存器&#xff0c;Mem表示NVM存储器 1、ENEC CCC ENEC 启用事件中断&#xff0c;ENEC CCC 仅在设备置于 I3C 基本模式后才受支持。当 SPD5 注册 ENEC CCC 时&am…

如何删除github中已经被追踪的文件

以.idea目录为例,如果在添加.gitignore规则之前已经将.idea目录或其下的某些文件添加到了版本控制中,那么这些文件将会被追踪,即使后来添加了.gitignore规则,它们仍然会出现在Changes中。您需要先将这些文件从版本控制中移除,然后再次提交。可以使用以下命令将已追踪的文件…

AcWing算法基础课——简单数据结构模板

说明 本篇文章只给出代码模板&#xff0c;以及自己对该模板的理解。如果想看正确的算法思路&#xff0c;可以移步AcWing官网看详情。链接&#xff1a;常用代码模板2——数据结构 - AcWing 如有错误&#xff0c;欢迎大家批评指正。 简单数据结构模板 一、链表 1.1 数组模拟单…

【CSS面试题】高度塌陷问题及解决

什么情况下产生 (when 父盒子没有定义高度&#xff0c;但是子元素有高度&#xff0c;希望用子盒子撑起父盒子的高度&#xff0c;但是子盒子添加了浮动属性之后&#xff0c;父盒子高度为0 <template><div class"father"><div class"son"&…

pyqt QPlainTextEdit 中捕获回车

pyqt QPlainTextEdit 中捕获回车 在PyQt的QPlainTextEdit控件中&#xff0c;可以通过重写keyPressEvent()函数来捕获键盘事件。当按下回车键时&#xff0c;会发送一个Key_Return信号&#xff0c;我们可以连接这个信号到自定义的槽函数上进行处理。 以下是示例代码&#xff1a;…

Java 继承与多态

一、继承 在Java中&#xff0c;继承是一种重要的面向对象编程概念&#xff0c;它允许一个类&#xff08;称为子类或派生类&#xff09;继承另一个类&#xff08;称为父类或基类&#xff09;的属性和方法。这意味着子类可以使用父类的成员变量和方法&#xff0c;并且可以添加自…

算法第二十六天-删除有序数组中的重复项Ⅱ

删除有序数组中的重复项 题目要求 解题思路 题目要求中提到原地修改&#xff0c;那么肯定需要一个指针指向当前即将放置元素的位置&#xff0c;需要另外一个指针向后遍历所有元素&#xff0c;所以[双指针]解法呼之欲出。 慢指针slow&#xff1a;指向当前元素放置的位置&…

F - Earn to Advance

解题思路 由于对于一点不知道后面得花费&#xff0c;所以无法决策当前是否要停下赚钱或要停下多久考虑一点&#xff0c;可以由其左上方的所有点到达所以从往前推&#xff0c;得出到的总花费然后考虑从之后不赚钱直接到最终所用次数和剩余钱若存在&#xff0c;在后面点赚钱更优…

Python错题集-8:AttributeError(找不到对应的对象的属性)

1问题描述 AttributeError: AxesSubplot object has no attribute arc 2代码详情 import matplotlib.pyplot as plt# 创建一个新的图形和坐标轴 fig, ax plt.subplots()# 定义弧线的参数 center (0.5, 0.5) # 圆心坐标 (x, y) width 1.0 # 半径 height 0.5 # 半径 ang…

沁恒CH32V307VCT6开发板记录---kalrry

沁恒CH32V307VCT6开发板记录---kalrry 一、官网申请开发板二、环境准备图一 一、官网申请开发板 1.沁恒官网 二、环境准备 1.数据手册&#xff1a;CH32V307DS0.PDF&#xff0c;CH32FV2x_V3xRM.PDF 2.CH32V307评估板说明及参考应用例程&#xff1a;CH32V307EVT_ZIP 3.集成开…

【kvm企业级虚拟化】之初级篇

目录 一、kvm介绍二、kvm软件安装三、GuestOS安装第一种方式&#xff1a;图形化安装第二种方式&#xff1a;命令行模式安装 四、GuestOS升级配置五、KVM存储六、磁盘格式七、创建磁盘文件八、KVM基本管理九、虚拟机克隆十、虚拟机快照十一、KVM网络管理1、kvm修改virbr0地址段&…

每日一面——封装、继承和多态

写前声明&#xff1a;参考链接 C面经、面试宝典 等 ✊✊✊每日一面——封装、继承和多态 一、简述一下什么是面向对象&#xff1f;二、讲一讲封装、继承、多态是什么&#xff1f;三、C的多态怎么实现&#xff1f;四、C中类成员的访问权限和继承权限问题五、C中的重载、重写&…