代码训练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

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

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用websocket +定时器简易的实现一个网络聊天室

其实原理非常简单,就是客户端用户通过websoket来连接websocket服务端。然后服务端,收集每个用户发出的消息, 进而将每条用户的消息通过广播的形式推送到每个连接到服务端的客户端。从而实现用户的实时聊天。 // TODO : 我主要是讲一下实现思路。并未完善其功能。 1.后端 依赖 …

2024年【电工(初级)】考试内容及电工(初级)考试报名

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 电工&#xff08;初级&#xff09;考试内容根据新电工&#xff08;初级&#xff09;考试大纲要求&#xff0c;安全生产模拟考试一点通将电工&#xff08;初级&#xff09;模拟考试试题进行汇编&#xff0c;组成一套电…

线程的魔法:揭开现代操作系统并发执行的面纱

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…

elasticsearch篇:RestClient操作

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

解读BOT攻击,探索灵活高效的防护之道

回顾早期的互联网应用&#xff0c;由于业务流量比较小&#xff0c;往往单台服务器就能满足负载需求。随着互联网的流量越来越大&#xff0c;单服务器已经不能满足业务需求&#xff0c;无论它优化得再好&#xff0c;都较难承受大量的访问压力。支持负载均衡的技术很多&#xff0…

openssl3.2 - exp - 选择最好的内建椭圆曲线

文章目录 openssl3.2 - exp - 选择最好的内建椭圆曲线概述笔记将 openssl ecparam -list_curves 实现迁移到自己的demo工程备注END openssl3.2 - exp - 选择最好的内建椭圆曲线 概述 在openssl中使用椭圆曲线, 只允许选择椭圆曲线的名字, 无法给定椭圆曲线的位数. 估计每种椭…

扩展学习|系统理解数字经济

文献来源&#xff1a;[1]肖静华,胡杨颂,吴瑶.成长品&#xff1a;数据驱动的企业与用户互动创新案例研究[J].管理世界,2020,36(03):183-205.DOI:10.19744/j.cnki.11-1235/f.2020.0041. [2]陈晓红,李杨扬,宋丽洁等.数字经济理论体系与研究展望[J].管理世界,2022,38(02):208-22413…

浅谈JUC的理解(含JUC知识体系图)

浅谈JUC的理解 一、前言感悟二、并发知识三、一年前回答四、补充体系回答五、补充层次回答六、碎碎念 本文除了说技术&#xff0c;更多的是在一个两年多开发经验的程序员视角下&#xff0c;记录下自己探索到的世界。 如有不妥之处&#xff0c;还请指正。共勉。 一、前言感悟 当…

力扣hot100:239.滑动窗口最大值(优先队列/单调队列)

本题是一个经典的单调队列题。不过用优先队列也能解决。 一、优先队列 在使用优先队列时&#xff0c;我们会遇到这样的问题&#xff1a;如何将一个目标数从优先队列中弹出&#xff1f;如果使用stl这是办不到的&#xff0c;虽然可以自行实现这样的功能。但是我们可以这样思考&am…

什么是GoogLeNet,亮点是什么,为什么是这个结构?

GooLeNet 亮点 最明显的亮点就是引入了Inception&#xff0c;初衷是多卷积核增加特征的多样性&#xff0c;提高泛化能力 &#xff0c;比如&#xff0c;最下边是一个输入层&#xff0c;然后这个输入分别传递给1*1&#xff0c;3 * 3 &#xff0c;5 * 5和一个最大池化层&#xff…

IP数据报格式

每一行都由32位比特&#xff0c;即4个字节组成&#xff0c;每个格子称为字段或者域。IP数据报由20字节的固定部分和最大40字节的可变部分组成。 总长度 总长度为16个比特&#xff0c;该字段的取值以字节为单位&#xff0c;用来表示IPv4数据报的长度(首部长度数据载荷长度)最大…

Long-term Correlation Tracking LCT 目标跟踪算法源码运行

资源 LCT-tracker项目地址VLFeat官网OpenCV下载地址OTB50数据集百度网盘资源 参考博客 一步一步教你跑lct-tracker&#xff08;Win10Matlab 2016bVisual Studio 2015&#xff09;LCT代码跑起来先文章思路总结 正文 1. 环境配置 我的环境&#xff1a;Win11、Visual Studio…

python+realsense

单目相机(RGB影像):分辨率&#xff1a;320180,320240,424240,640360,640480,848480,960540,1280720,19201080&#xff1b;帧率&#xff1a;6,15,30,60 按照博文Python实战之Realsense_realsense python-CSDN博客的代码显示如下&#xff08;我更改了分辨率和帧率&#xff0c;大…

设计模式:观察者模式 ⑧

一、思想 观察者模式是一种常见的设计模式&#xff0c;也称作发布-订阅模式。它主要解决了对象之间的通知依赖关系问题。在这种模式中&#xff0c;一个对象&#xff08;称作Subject&#xff09;维护着一个对象列表&#xff0c;这些对象&#xff08;称作Observers&#xff09;都…

css3中nth-child属性作用及用法剖析

hello宝子们...我们是艾斯视觉擅长ui设计和前端开发10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩! 标题&#xff1a;CSS3中nth-child属性作用及用法剖析 摘要&#xff1a;CSS3中的nth-child选择器允许我们根据元素位置来定位特定的元素…

Vue3中Vue Router的使用区别

在 Vue 3 中&#xff0c;useRouter 和 useRoute 是两个用于 Vue Router 的 Composition API 函数&#xff0c;它们的用途和返回的对象不同&#xff0c;接下来详细了解一下它们的区别以及如何正确使用它们。 useRouter useRouter 用于获取 router 实例&#xff0c;这个实例提供…

python(5)之处理数组

上次代码结果如下&#xff1a; 1、处理数组的缺失值 1、isnan&#xff08;&#xff09;函数 isnan&#xff08;&#xff09;函数是Numpy模块里的一个可以标记数组中缺失值的位置 代码示例如下&#xff1a; import numpy as np ac np.array([1,2,3,2,3,4,5,9,np.nan,1]) p…

OSPF收发报文实验简述

1、OSPF采用组播形式收发报文&#xff0c;这样可以减少对其它不运行OSPF路由器的影响。 通过wireshark软件对r2 e0/0/0 端口进行数据抓包&#xff0c;发现224.0.0.5为组播地址&#xff0c;如下图

深入了解二叉搜索树:原理、实现与应用

目录 一、介绍二叉搜索树 二、二叉搜索树的基本性质 三、二叉搜索树的实现 四、总结 在计算机科学中&#xff0c;数据结构是构建算法和程序的基础。其中&#xff0c;二叉搜索树&#xff08;Binary Search Tree&#xff0c;简称 BST&#xff09;作为一种常见的数据结构&#…

力扣图论篇

以下思路来自代码随想录以及官方题解。 文章目录 797.所有可能的路径200.岛屿数量130.被围绕的区域1020.飞地的数量 797.所有可能的路径 给你一个有 n 个节点的 有向无环图&#xff08;DAG&#xff09;&#xff0c;请你找出所有从节点 0 到节点 n-1 的路径并输出&#xff08;不…