当前位置: 首页 > article >正文

【C++】原地删除有序数组重复元素:两种解法的深度剖析

一、问题描述题目要求给定一个非严格递增排列的整数数组nums需要原地删除重复出现的元素使得每个元素只出现一次并返回删除后数组的新长度。具体要求元素的相对顺序必须保持一致返回唯一元素的数量 k数组的前 k 个元素应包含去重后的唯一数字下标 k-1 之后的元素可以忽略示例cpp输入: nums [1,1,2] 输出: 2, nums [1,2,_] 解释: 函数应返回长度 2原数组的前两个元素修改为 [1,2] 输入: nums [0,0,1,1,1,2,2,3,3,4] 输出: 5, nums [0,1,2,3,4,_,_,_,_,_]二、解法一迭代器与 erase 操作代码实现cppclass Solution { public: int removeDuplicates(vectorint nums) { vectorint:: iterator it nums.begin(); it; // 从第二个元素开始 while(it ! nums.end()) { if((*it) *(it - 1)) // 当前元素与前一个元素相同 { it nums.erase(it); // 删除当前元素 } else { it; // 移动到下一个元素 } } return nums.size(); } };算法分析1. 核心思路使用迭代器遍历数组比较当前元素与前一个元素如果相同使用erase删除当前元素如果不同移动到下一个元素2. 关键细节cpp// 为什么从第二个元素开始 // 第一个元素无需与前面的元素比较没有前面的元素 it; // it 初始指向 nums.begin()it 后指向第二个元素 // 为什么比较 it 和 it-1 // 因为数组已排序重复元素相邻 if((*it) *(it - 1)) // erase 的返回值是什么 // erase 返回指向被删除元素之后元素的迭代器 it nums.erase(it); // 删除后 it 自动指向下一个有效元素3. 执行流程示例text初始: [1,1,2,2,3] ↑ it (指向第二个1) 步骤1: it指向1与前一个1相同 → 删除 → [1,2,2,3] 步骤2: it指向2与前一个1不同 → it → [1,2,2,3] 步骤3: it指向第二个2与前一个2相同 → 删除 → [1,2,3] 步骤4: it指向3与前一个2不同 → it → 到达end 结果: [1,2,3]长度34. 时间复杂度分析最坏情况: O(n²)erase操作需要移动后续所有元素假设删除 n 个元素每次平均移动 n/2 个元素平均情况: O(n²)空间复杂度: O(1)原地操作5. 优缺点优点:代码直观易于理解真正修改了数组大小不需要额外空间缺点:效率低频繁移动元素不适合大数据量不是最优解三、解法二双指针法推荐代码实现cppclass Solution { public: int removeDuplicates(vectorint nums) { if (nums.empty()) return 0; int slow 0; // 慢指针指向已去重部分的末尾 for (int fast 1; fast nums.size(); fast) { // 当快指针指向的元素与慢指针不同时 if (nums[fast] ! nums[slow]) { slow; // 慢指针前进 nums[slow] nums[fast]; // 复制不重复的元素 } // 如果相同快指针继续前进慢指针不动 } return slow 1; // 返回新长度 } };算法分析1. 核心思想使用两个指针快指针fast和慢指针slowslow指向已去重部分的最后一个元素fast遍历整个数组当发现新元素时将其复制到slow1的位置2. 可视化演示text初始状态: [0,0,1,1,1,2,2,3,3,4] s f 步骤1: f1, nums[f]0, nums[s]0 → 相同 → f [0,0,1,1,1,2,2,3,3,4] s f 步骤2: f2, nums[f]1, nums[s]0 → 不同 → s, nums[s]nums[f] [0,1,1,1,1,2,2,3,3,4] s f 步骤3: f3, nums[f]1, nums[s]1 → 相同 → f [0,1,1,1,1,2,2,3,3,4] s f 步骤4: f4, nums[f]1, nums[s]1 → 相同 → f [0,1,1,1,1,2,2,3,3,4] s f 步骤5: f5, nums[f]2, nums[s]1 → 不同 → s, nums[s]nums[f] [0,1,2,1,1,2,2,3,3,4] s f ... 以此类推最终得到 [0,1,2,3,4]3. 时间复杂度分析时间复杂度: O(n)只需遍历数组一次每个元素最多被访问两次空间复杂度: O(1)只使用两个指针变量4. 边界情况处理cpp// 空数组 if (nums.empty()) return 0; // 单元素数组 // 直接返回1无需任何操作 // 所有元素都相同 // slow始终为0返回1四、两种解法的对比性能对比表特性迭代器erase法双指针法时间复杂度O(n²)O(n)空间复杂度O(1)O(1)是否真正删除是否只是覆盖代码复杂度中等简单执行效率低高适用场景小数据量所有场景内存操作对比cpp// 方法一erase操作的内存变化 [1,1,2,2,3] // 原始 [1,2,2,3] // 删除第一个重复1移动后面3个元素 [1,2,3] // 删除重复2移动后面1个元素 // 总共移动4个元素 // 方法二双指针法的内存变化 [1,1,2,2,3] // 原始 [1,2,2,3,3] // 第一次复制fast2, slow1 [1,2,3,3,3] // 第二次复制fast4, slow2 // 没有元素移动只有覆盖操作五、常见错误与陷阱错误1迭代器越界cpp// 错误的写法 while(it ! nums.end()) { if(*(it) *(it 1)) { // 当it指向最后一个元素时it1越界 it nums.erase(it); } it; }错误2逻辑错误cpp// 错误的写法 while(it ! nums.end()) { if((*it) *(it - 1)) { nums.erase(it); // 没有接收返回值it失效 } it; // 使用失效的迭代器 }错误3未处理空数组cpp// 需要处理空数组 if (nums.empty()) return 0;六、扩展思考1. 如果数组未排序怎么办cpp// 需要先排序但会改变相对顺序 sort(nums.begin(), nums.end()); // 然后再去重2. 如果允许每个元素最多出现两次怎么办cppclass Solution { public: int removeDuplicates(vectorint nums) { if (nums.size() 2) return nums.size(); int slow 2; // 从第三个元素开始 for (int fast 2; fast nums.size(); fast) { // 比较当前元素与slow-2位置的元素 if (nums[fast] ! nums[slow - 2]) { nums[slow] nums[fast]; slow; } } return slow; } };3. 如果要去重并保留原顺序未排序数组cpp// 使用哈希表记录已出现元素 int removeDuplicatesUnordered(vectorint nums) { unordered_setint seen; int slow 0; for (int fast 0; fast nums.size(); fast) { if (seen.find(nums[fast]) seen.end()) { seen.insert(nums[fast]); nums[slow] nums[fast]; slow; } } return slow; }七、实际应用场景1. 数据库查询结果去重sql-- SQL中的DISTINCT类似于数组去重 SELECT DISTINCT column_name FROM table_name;2. 日志分析去除重复的日志条目统计唯一IP地址分析用户唯一访问量3. 数据清洗去除重复记录整理有序数据集准备机器学习训练数据4. 内存优化减少内存占用提高缓存效率优化数据处理流水线八、性能测试测试代码cppvoid testPerformance() { // 生成测试数据100万个有序整数有大量重复 vectorint nums; for (int i 0; i 1000000; i) { nums.push_back(i / 100); // 每个数字重复100次 } vectorint nums1 nums; vectorint nums2 nums; // 测试方法一 auto start chrono::high_resolution_clock::now(); Solution().removeDuplicatesErase(nums1); auto end chrono::high_resolution_clock::now(); auto duration1 chrono::duration_castchrono::milliseconds(end - start); // 测试方法二 start chrono::high_resolution_clock::now(); Solution().removeDuplicatesTwoPointers(nums2); end chrono::high_resolution_clock::now(); auto duration2 chrono::duration_castchrono::milliseconds(end - start); cout 方法一erase用时: duration1.count() ms endl; cout 方法二双指针用时: duration2.count() ms endl; }预期结果text方法一erase用时: 约5000ms 方法二双指针用时: 约10ms九、总结与建议1. 算法选择建议面试场景优先展示双指针法体现算法优化思维小数据量两种方法都可erase法代码更直观大数据量必须使用双指针法工程实践优先考虑效率和可维护性2. 学习要点理解原地操作的含义和限制掌握迭代器的正确使用方法熟练应用双指针技巧分析算法的时间空间复杂度3. 类似题目推荐删除排序数组中的重复项 II每个元素最多出现两次移动零将0移动到末尾保持非零元素顺序移除元素移除特定值返回新长度合并两个有序数组4. 最终建议在实际编程和面试中双指针法是解决这类问题的首选。它不仅效率高而且思路清晰代码简洁。理解并掌握这种思想能够解决一大类数组操作问题。记住好的算法应该是高效且优雅的双指针法正是这样的典范。

相关文章:

【C++】原地删除有序数组重复元素:两种解法的深度剖析

一、问题描述题目要求给定一个非严格递增排列的整数数组 nums,需要原地删除重复出现的元素,使得每个元素只出现一次,并返回删除后数组的新长度。具体要求元素的相对顺序必须保持一致返回唯一元素的数量 k数组的前 k 个元素应包含去重后的唯一…...

揭秘Cursor-Free-VIP:如何突破AI编码工具的机器ID限制实现永久免费使用

揭秘Cursor-Free-VIP:如何突破AI编码工具的机器ID限制实现永久免费使用 【免费下载链接】cursor-free-vip [Support 0.45](Multi Language 多语言)自动注册 Cursor Ai ,自动重置机器ID , 免费升级使用Pro 功能: Youve …...

LSPatch实战教程:如何为APK文件嵌入Xposed模块

LSPatch实战教程:如何为APK文件嵌入Xposed模块 【免费下载链接】LSPatch A non-root Xposed framework extending from LSPosed 项目地址: https://gitcode.com/gh_mirrors/lsp/LSPatch LSPatch是一款强大的非Root Xposed框架,源自LSPosed项目&am…...

ant-design-vue Table+Form实现动态表单验证:自定义规则与必填项触发实战

1. 动态表单验证的核心场景 在管理后台开发中,表格内嵌表单的需求非常常见。比如我们需要批量编辑商品信息,或者动态添加多行联系人数据时,传统的做法是在表格外单独做表单,但这样会导致操作流程割裂。ant-design-vue的TableForm组…...

避坑指南:STM32WLE5CCU6移植LoRaWAN节点,搞定BSP报错、信道配置与OTAA入网参数

STM32WLE5CCU6 LoRaWAN节点移植实战:从BSP报错到OTAA入网的完整避坑手册 去年第一次接触STM32WLE5系列芯片时,我花了整整三天时间才让LoRaWAN节点成功入网。期间遇到的BSP缺失、信道配置错误、OTAA参数无效等问题,几乎踩遍了所有新手可能遇到…...

Unity UI布局核心:从RectTransform的localPosition与anchoredPosition看父子坐标系

1. RectTransform基础概念解析 在Unity的UI系统中,RectTransform就像是一个魔法尺子,它不仅能测量UI元素的大小和位置,还能决定这个元素如何"粘"在它的父元素上。想象一下你在布置房间:RectTransform就是那个告诉你&quo…...

【2026年】新大纲普通话考试真题题库50套(PDF电子版)

2026年国家普通话水平测试新大纲及配套资源说明 大纲更新要点 自2024年1月1日起,国家语言文字工作委员会正式实施《普通话水平测试新版大纲》。本次修订对测试内容与形式进行了系统性优化,明确规定了以下核心组成部分: 朗读短文&#xff1…...

终极暗黑2存档编辑器指南:如何快速打造完美游戏角色

终极暗黑2存档编辑器指南:如何快速打造完美游戏角色 【免费下载链接】d2s-editor 项目地址: https://gitcode.com/gh_mirrors/d2/d2s-editor 你是否曾经花费数小时刷装备,结果却一无所获?或是角色属性点分配不当,导致后期…...

VirtualBox 7.0 保姆级教程:手把手教你给Win10虚拟机装“显卡驱动”(增强功能详解)

VirtualBox 7.0 性能优化全攻略:解锁Win10虚拟机的完整潜能 当你第一次在VirtualBox中成功运行Win10虚拟机时,那种兴奋感可能很快会被一些不便所取代——窗口无法自适应缩放、文件传输需要繁琐的共享设置、显示效果总是差强人意。这些问题背后&#xff0…...

Navicat重置脚本终极指南:3种简单方法无限恢复试用期

Navicat重置脚本终极指南:3种简单方法无限恢复试用期 【免费下载链接】navicat_reset_mac navicat mac版无限重置试用期脚本 Navicat Mac Version Unlimited Trial Reset Script 项目地址: https://gitcode.com/gh_mirrors/na/navicat_reset_mac 你是否正在寻…...

保姆级教程:用Helm在K8s上部署RustFS对象存储(含Local Path配置与Ingress暴露)

Kubernetes实战:基于Helm与Local Path的RustFS对象存储部署指南 当企业需要构建私有化对象存储解决方案时,兼容S3协议的开源存储系统成为热门选择。本文将手把手带您完成RustFS在Kubernetes集群中的生产级部署,涵盖从底层磁盘准备到Ingress暴…...

ActiveMQ与微服务架构集成:构建分布式系统通信解决方案

ActiveMQ与微服务架构集成:构建分布式系统通信解决方案 【免费下载链接】activemq Apache ActiveMQ 项目地址: https://gitcode.com/gh_mirrors/ac/activemq Apache ActiveMQ作为一款强大的消息中间件,为微服务架构提供了可靠的异步通信支持&…...

Qwen3.5-9B合规部署:GDPR数据不出境+对话记录加密存储方案

Qwen3.5-9B合规部署:GDPR数据不出境对话记录加密存储方案 1. 项目概述 Qwen3.5-9B是一款拥有90亿参数的开源大语言模型,具备强大的逻辑推理、代码生成和多轮对话能力。该模型支持多模态理解(图文输入)和长上下文处理&#xff08…...

Phi-4-mini-reasoning Chainlit A/B测试:不同系统提示词对推理质量影响分析

Phi-4-mini-reasoning Chainlit A/B测试:不同系统提示词对推理质量影响分析 1. 模型介绍与部署验证 1.1 Phi-4-mini-reasoning模型特点 Phi-4-mini-reasoning是一个专注于高质量推理能力的轻量级开源模型,具有以下核心特性: 推理能力优化…...

Pharos Network联合港大金融科技学院,启动AI决策研究项目,深化Layer1与学术融合

香港,2026年4月15日 —— 专注于机构级别的金融型 Layer 1公链 Pharos Network 宣布,与香港大学渣打慈善基金金融科技学院 共同在香港大学商学院硕士课程Capstone Proiect框架下开展的学术与产业联合研究合作,同时与 Pharos 生态孵化体系形成…...

⚖️Lychee-Rerank效果展示:跨境电商多语言Query(中/英/日)与商品描述匹配案例

Lychee-Rerank效果展示:跨境电商多语言Query与商品描述匹配案例 1. 引言:当搜索遇到多语言难题 想象一下这个场景:你是一家跨境电商平台的运营人员,每天要处理成千上万的商品搜索请求。用户可能用中文搜索“无线蓝牙耳机”&…...

FireRed-OCR Studio惊艳效果:专利文件权利要求书层级结构精准识别

FireRed-OCR Studio惊艳效果:专利文件权利要求书层级结构精准识别 1. 引言:当文档解析遇到专利权利要求书 想象一下,你面前有一份长达几十页的专利文件,其中最关键的部分——权利要求书——采用了复杂的层级结构:独立…...

2026年中国词元经济产业链全景分析报告

2026年以来,AI应用场景持续破圈,从春节AI红包到OpenClaw “全民养虾” 等现象级事件席卷全球,人工智能正式从交互对话走向自主执行的智能体时代,带动行业需求迎来爆发式增长。在此背景下,词元作为 AI 运行与服务交互的…...

别再只会用VLC了!手把手教你用Python+OpenCV调用UVC摄像头(附完整代码)

PythonOpenCV调用UVC摄像头实战指南 在计算机视觉项目中,USB摄像头是最常用的图像采集设备之一。但很多开发者仅仅停留在使用VLC等现成软件查看画面的阶段,没有充分发挥UVC协议提供的丰富控制功能。本文将带你深入探索如何用PythonOpenCV直接调用UVC摄像…...

【实战解析】【立体匹配系列】AD-Census代价计算:从公式到代码的深度剖析

1. AD-Census算法背景与核心思想 AD-Census算法最早由中国学者Xing Mei等人在2011年ICCV会议上提出,这篇名为《On Building an Accurate Stereo Matching System on Graphics Hardware》的论文,为立体匹配领域带来了一个高效且效果出色的解决方案。你可能…...

企业级Nacos定制全攻略:从logo替换到服务地址穿透的完整解决方案

企业级Nacos深度定制实战:打造专属服务发现平台 在数字化转型浪潮中,服务发现组件已成为现代微服务架构的核心基础设施。作为阿里巴巴开源的明星项目,Nacos凭借其服务发现、配置管理和服务治理三位一体的能力,正逐步取代Eureka成…...

ARM Cortex-M开发避坑指南:DMB、DSB、ISB这三个内存屏障指令到底什么时候用?

ARM Cortex-M开发实战:DMB/DSB/ISB内存屏障指令深度解析与避坑指南 在嵌入式开发领域,尤其是基于ARM Cortex-M系列处理器的项目中,内存屏障指令就像交通信号灯一样默默维持着系统运行的秩序。许多工程师虽然知道DMB、DSB、ISB这三个指令的存在…...

如何从零打造一个高性价比的DIY蓝牙音箱?

1. 为什么选择DIY蓝牙音箱? 每次看到商场里动辄上千元的蓝牙音箱,我都会想:这东西真的值这个价吗?拆开看过几款主流产品后更确信,大部分成本其实花在了品牌溢价和外观设计上。三年前我第一次尝试自制蓝牙音箱&#xff…...

光伏电站运维必看:MPPT控制器参数怎么调?这5个坑你踩过几个?

光伏电站MPPT控制器实战调参指南:5个高频运维陷阱与破解方案 清晨六点,青海某光伏电站的监控系统发出警报——3号阵列发电量骤降23%。运维团队排查两小时才发现,问题竟出在MPPT控制器的电压扰动步长设置:默认参数在高原晨间快速变…...

FaceRecon-3D实战教程:构建人脸3D资产库的自动化Pipeline设计

FaceRecon-3D实战教程:构建人脸3D资产库的自动化Pipeline设计 1. 引言:从一张照片到3D资产 想象一下,你手头有成千上万张人物照片,可能是员工证件照、客户头像或者历史人物肖像。传统上,要把这些2D照片变成3D模型&am…...

TI盘古开发板+蓝牙模块:手把手教你实现无人机与消防小车的空地协同通信(附完整代码)

TI盘古开发板与蓝牙模块实战:空地协同通信系统开发全解析 1. 空地协同系统架构设计 在智能消防、农业巡检和工业监测等领域,无人机与地面设备的协同作业正成为技术热点。这套系统的核心在于建立稳定可靠的通信链路,实现实时数据交换与任务协…...

面试官: 主键索引特点解析(答案深度解析)持续更新

主键索引特点 —— 面试官想听的「底层逻辑」和「踩坑经验」⚠️ 注意:面试中只答“唯一、非空、聚簇索引”是及格线;真正拉开差距的,是你能否讲清 “为什么必须这样设计?”、“不这么干会怎样?”、“实际开发中哪些坑…...

2025届毕业生推荐的降AI率工具推荐

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 为能切实有效地把内容被判定为AIGC的可能性降低,我们能够运用下面这一连串的策略…...

图像质量评价指标全解析:SROCC、PLCC、KROCC到底怎么选?

图像质量评价指标全解析:SROCC、PLCC、KROCC到底怎么选? 在计算机视觉和图像处理领域,图像质量评价(IQA)是算法开发和性能验证的关键环节。无论是开发新的图像增强算法,还是评估不同压缩技术对画质的影响,我们都需要可…...

2025届最火的六大降重复率方案推荐榜单

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 减弱机器生成所呈现出的规律性特性,是降低AIGC检测率的关键所在。其一&#xff0…...