代码训练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.hqwc.cn/news/527755.html

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

相关文章

Redis进阶--一篇文章带你走出Redis

目录 什么是Redis?? Redis有哪些使用场景? Redis是单线程还是多线程? 为什么Redis是单线程速度还是很快?? Redis持久化 RDB机制:(Redis DataBase) [是redis中默认的持久化方式] AOF机制:(Append Only File) Redis和MySQL如何保持数据一致????…

Vue 监听器:让你的应用实时响应变化

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

IDEA编译安卓源码TVBox

因为电视x受限&#xff0c;无法观看电视直播&#xff0c;为了春晚不受影响&#xff0c;于是网络一顿搜索&#xff0c;试过多个APP&#xff0c;偶尔找到这款开源的TVBox&#xff0c;寒假在家&#xff0c;随便拿来练练手&#xff0c;学习安卓APP的编写&#xff0c;此文做以记录&a…

新质生产力助春播春管:佳格天地连续第5年上线大数据平台,服务春季生产

随着“惊蛰”节气过去,全国各地陆续掀起春播春管热潮。今年的政府工作报告中指出,2023年我国粮食产量1.39万亿斤,再创新高。2024年要坚持不懈抓好“三农”工作,扎实推进乡村全面振兴,粮食产量预期目标1.3万亿斤以上。 粮食产量预期目标的明确为一年农事生产指引了方向。同时,新…

elasticsearch篇:RestClient操作

1. RestClient ES官方提供了各种不同语言的客户端&#xff0c;用来操作ES。这些客户端的本质就是组装DSL语句&#xff0c;通过http请求发送给ES。官方文档地址&#xff1a;Elasticsearch Clients | Elastic 其中的Java Rest Client又包括两种&#xff1a; Java Low Level Res…

LVS集群 ----------------(直接路由 )DR模式部署 (二)

一、LVS集群的三种工作模式 lvs-nat&#xff1a;修改请求报文的目标IP,多目标IP的DNAT lvs-dr&#xff1a;操纵封装新的MAC地址&#xff08;直接路由&#xff09; lvs-tun&#xff1a;隧道模式 lvs-dr 是 LVS集群的 默认工作模式 NAT通过网络地址转换实现的虚拟服务器&…

Linux--文件类型与权限

Linux上一切皆文件; 蓝色的是目录文件 Linux不以文件的扩展名来区分文件类型,而是在文件属性中有一列专门记录文件类型. ls -l 可以查看 普通文件:.c .cpp .h .txt .pdf .xls (-) 目录文件:文件夹 (d) 管道文件:用于进程间通信的一种文件 (p) 链接文件:相当于W…

学习笔记。。。

1.字符串的拼接 1.sprintf() 往字符串的前面或中间、后面拼接一个字符串。 2.strncpy()用来复制字符串的前n个字符 //dest为目标数组&#xff0c;src为源数组&#xff0c;n为要复制的字符个数 2.char* My_strncpy(char* dest, const char* src, int n) 3.char *strcat(ch…

【研发管理】梳理研发部门的商业画布和常见问题

导读&#xff1a;基于商业画布应用到职能部门分析&#xff0c;从一个新的视角审视带来不一样的思考。本人以研发部门为例&#xff0c;梳理商业画布和常见问题&#xff0c;仅供参考。 目录 1、画布总览​编辑 2、沟通渠道问题 3、客户关系问题 4、核心资源问题 5、关键业务…

linux操作系统虚拟机的环境配置

目录 一、虚拟机安装&#xff08;类似硬件的安装&#xff09; &#xff08;1&#xff09;创建虚拟机 &#xff08;2&#xff09;创建虚拟机 二、IP和主机名称配置 1、设置VM上的IP 2、设置我们电脑上VMnet8的IP 3、设置虚拟机上的IP 主机名称映射 以下是设置主机名映射…

Qt编程技巧总结篇(1)- 画图方面

文章目录 Qt编程技巧总结篇&#xff08;1&#xff09;鼠标左键显示坐标位置鼠标右键拖拽鼠标滚轮放大缩小 小结 Qt编程技巧总结篇&#xff08;1&#xff09; 看了许多的关于C的Qt编程blog&#xff0c;但总有点乱&#xff0c;现在抽出时间对其中关于画图的一些函数进行梳理&…

嵌入式学习day34 网络

TCP包头: 1.序号:发送端发送数据包的编号 2.确认号:已经确认接收到的数据的编号(只有当ACK为1时,确认号才有用) TCP为什么安全可靠: 1.在通信前建立三次握手连接 SYN SYNACK ACK 2.在通信过程中通过序列号和确认号保障数据传输的完整性 本次发送序列号:上次…