news 2026/4/15 11:15:35

力扣26.有序数组去重:HashSet vs 双指针法

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
力扣26.有序数组去重:HashSet vs 双指针法

问题描述

给定一个有序的整数数组,我们需要原地移除所有重复元素,使得每个元素只出现一次,并返回新数组的长度。要求不使用额外的数组空间,空间复杂度为O(1)。

示例:

输入:nums = [1,1,2,2,3,4,4,5] 输出:5, nums = [1,2,3,4,5,...]

方法一:HashSet

HashSet是Java集合框架中的一个类,它实现了Set接口,底层基于哈希表实现。HashSet的核心特性是不允许重复元素,且不保证元素的顺序

代码实现

import java.util.HashSet; import java.util.Iterator; public class HashSetSolution { public int removeDuplicates(int[] nums) { // 边界条件检查 if (nums == null || nums.length == 0) { return 0; } // 创建HashSet存储唯一元素 HashSet<Integer> uniqueSet = new HashSet<>(); // 遍历原数组,将元素添加到HashSet中 // HashSet会自动去重,重复元素不会被添加 for (int num : nums) { uniqueSet.add(num); } // 将Set中的元素复制回数组 // 注意:HashSet不保证顺序,所以结果可能不是有序的 int index = 0; Iterator<Integer> iterator = uniqueSet.iterator(); while (iterator.hasNext()) { nums[index++] = iterator.next(); } // 返回唯一元素的数量 return uniqueSet.size(); } }

算法分析

时间复杂度
  • 添加元素到HashSet:O(n)

  • 遍历HashSet:O(k),其中k是唯一元素的数量

  • 总时间复杂度:O(n)

空间复杂度
  • HashSet需要存储所有唯一元素:O(k) ≈ O(n)(最坏情况)

优点
  1. 代码简洁:逻辑直观,易于理解

  2. 通用性强:适用于无序数组的去重

  3. 自动去重:利用Java集合框架的特性

缺点
  1. 破坏顺序:HashSet不保证元素的插入顺序,对于有序数组会打乱原始顺序

  2. 额外空间:需要O(n)的额外空间,不符合题目原地修改的要求

  3. 性能开销:哈希表操作有一定的开销,包括哈希计算、处理哈希冲突等

适用场景

  1. 当数组无序且需要去重时

  2. 当不关心元素顺序时

  3. 当内存空间充足,可以接受O(n)额外空间时

方法二:双指针法(推荐)

双指针法是一种经典的原地算法技巧。我们使用两个指针:

  • 快指针:遍历整个数组,寻找新的不重复元素

  • 慢指针:指向下一个不重复元素应该存放的位置

由于数组是有序的,重复元素必然相邻,我们可以利用这个特性高效去重。

代码实现

public class TwoPointerSolution { public int removeDuplicates(int[] nums) { // 边界条件检查 if (nums == null || nums.length == 0) { return 0; } // 慢指针,指向下一个不重复元素应该存放的位置 // 初始为1,因为第一个元素肯定不需要检查 int slow = 1; // 快指针,遍历整个数组 for (int fast = 1; fast < nums.length; fast++) { // 如果当前元素不等于前一个元素,说明找到了新的不重复元素 if (nums[fast] != nums[fast - 1]) { // 将不重复元素复制到慢指针的位置 nums[slow] = nums[fast]; // 慢指针前进 slow++; } // 快指针始终前进 } // 慢指针的值就是新数组的长度 return slow; } // 更简洁的写法 public int removeDuplicatesConcise(int[] nums) { if (nums.length == 0) return 0; int i = 0; for (int j = 1; j < nums.length; j++) { if (nums[j] != nums[i]) { i++; nums[i] = nums[j]; } } return i + 1; } }

算法分析

时间复杂度
  • 只需要遍历数组一次:O(n)

空间复杂度
  • 只使用了几个指针变量:O(1)

优点
  1. 原地修改:不需要额外空间,符合题目要求

  2. 保持顺序:保持原始数组的有序性

  3. 性能高效:只需要一次遍历,没有哈希计算开销

  4. 内存友好:适合处理大规模数据

缺点
  1. 仅适用于有序数组:对于无序数组无效

  2. 需要手动实现:相比HashSet,代码需要自己控制指针

工作原理详解

让我们通过一个例子来理解双指针法:

初始数组:[1, 1, 2, 2, 3, 4, 4, 5]

步骤:

  1. 初始化:slow = 1,fast = 1

  2. fast=1: nums[1]=1, nums[0]=1,相等,跳过

  3. fast=2: nums[2]=2, nums[1]=1,不相等,复制到slow=1,slow=2

  4. fast=3: nums[3]=2, nums[2]=2,相等,跳过

  5. fast=4: nums[4]=3, nums[3]=2,不相等,复制到slow=2,slow=3

  6. 以此类推...

最终结果:前5个元素为[1, 2, 3, 4, 5],返回5

对比分析

特性HashSet法双指针法
时间复杂度O(n)O(n)
空间复杂度O(n)O(1)
是否保持顺序
是否原地修改
适用数组类型任意数组有序数组
代码复杂度简单中等
内存使用
性能表现良好(哈希计算开销)优秀

实际应用场景

适合使用HashSet的场景

  1. 处理日志去重:日志数据通常无序,且不关心顺序

  2. 用户ID去重:用户ID列表需要快速去重,且顺序不重要

  3. 数据预处理:在数据清洗阶段,需要快速识别和去重

适合使用双指针的场景

  1. 数据库查询结果去重:数据库查询结果通常有序

  2. 时间序列数据处理:时间序列数据天然有序

  3. 算法竞赛:对内存和性能有严格要求

  4. 嵌入式系统:内存受限的环境

变种问题与扩展

问题变种:保留最多k个重复元素

java

public int removeDuplicatesK(int[] nums, int k) { if (nums.length <= k) return nums.length; int slow = k; // 前k个元素肯定保留 for (int fast = k; fast < nums.length; fast++) { // 检查当前元素是否与slow-k位置的元素不同 if (nums[fast] != nums[slow - k]) { nums[slow] = nums[fast]; slow++; } } return slow; }

问题变种:无序数组去重(要求原地)

java

public int removeDuplicatesUnordered(int[] nums) { // 先排序,再使用双指针法 Arrays.sort(nums); return removeDuplicates(nums); }

最佳实践建议

  1. 分析问题特性:首先判断数组是否有序

  2. 考虑内存限制:如果内存受限,优先考虑双指针法

  3. 考虑顺序要求:如果需要保持顺序,选择双指针法

  4. 代码可读性:在团队开发中,选择易于理解和维护的方法

  5. 性能需求:对于性能敏感的场景,进行基准测试

总结

在有序数组去重的问题中,双指针法无疑是更优的选择。它不仅满足题目要求的原地修改和O(1)空间复杂度,而且保持数组有序,性能优异。虽然HashSet法代码更简洁,但其破坏顺序和额外空间开销的缺点使其不适用于此问题。

关键启示

  • 没有绝对"最好"的算法,只有最适合特定场景的算法

  • 理解问题约束是选择算法的关键

  • 在面试中,不仅要写出解决方案,还要能解释为什么选择这种方法

掌握这两种方法,不仅能够解决有序数组去重问题,还能为处理更复杂的数组操作问题打下坚实基础。在实际开发中,根据具体需求选择合适的算法,是每个开发者必备的技能。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/8 8:12:11

Unity游戏汉化实战:XUnity.AutoTranslator实时翻译配置完全指南

Unity游戏汉化实战&#xff1a;XUnity.AutoTranslator实时翻译配置完全指南 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 你是否曾经因为语言障碍而错失心爱的海外Unity游戏&#xff1f;面对满屏的外文…

作者头像 李华
网站建设 2026/4/15 10:36:12

高密度PCB设计中过孔电流选型注意事项

高密度PCB设计中&#xff0c;过孔不只是“打个洞”那么简单你有没有遇到过这样的情况&#xff1a;电路板明明走线都通了&#xff0c;电源也接上了&#xff0c;结果一上电&#xff0c;系统跑着跑着就重启&#xff1f;或者更糟——某个角落开始冒烟&#xff1f;如果你正在做高密度…

作者头像 李华
网站建设 2026/4/11 6:25:16

如何降低AI生成内容的AI率和AI痕迹?推荐4款超好用的神器

现在AI写作太普遍了&#xff0c;不少同学会用它辅助写论文&#xff0c;但学校那边的检测也越来越严——不仅要查重复率&#xff0c;还要查“AI率”。要是AI率超标&#xff0c;论文可能就卡住了&#xff0c;这确实让人头疼。自己逐字逐句改吧&#xff0c;费时费力&#xff0c;还…

作者头像 李华
网站建设 2026/4/15 5:32:23

Unity游戏多语言解决方案:XUnity Auto Translator技术解析

Unity游戏多语言解决方案&#xff1a;XUnity Auto Translator技术解析 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 在全球游戏市场不断融合的今天&#xff0c;语言障碍成为制约玩家体验的重要因素。XU…

作者头像 李华
网站建设 2026/4/13 14:23:16

XUnity自动翻译插件:游戏语言障碍的智能解决方案

XUnity自动翻译插件&#xff1a;游戏语言障碍的智能解决方案 【免费下载链接】XUnity.AutoTranslator 项目地址: https://gitcode.com/gh_mirrors/xu/XUnity.AutoTranslator 对于热爱全球Unity游戏的玩家来说&#xff0c;语言不通常常成为体验优秀作品的巨大障碍。XUni…

作者头像 李华