发布时间:2026/9/7 12:14:36
原地哈希算法详解:O(1)空间复杂度解决数组统计问题 这次我们来看一个算法训练营的习题——原地哈希。这个题目来自27代码打卡营第八周的第三题重点不是概念多复杂而是能不能在实际编码中快速识别适用场景、掌握实现套路。原地哈希的核心价值在于它能在O(1)的额外空间复杂度下解决数组元素与索引映射类问题。如果你正在准备技术面试或者想提升对数组操作的敏感度这篇文章会带你完成从问题识别到代码实现的完整闭环。本文会重点拆解原地哈希的适用场景、实现模板、边界处理并给出可直接运行的Python代码。我们会通过几个典型例题让你掌握如何在不使用额外哈希表的情况下通过数组本身的空间完成元素统计、重复检测或缺失值查找。1. 原地哈希核心能力速览能力项说明空间复杂度O(1)仅使用输入数组本身的空间时间复杂度通常为O(n)n为数组长度适用问题元素范围已知的数组统计类问题典型场景查找重复元素、缺失数字、第一个缺失正数等实现关键利用数组索引作为隐含的哈希键前置条件数组元素可映射到有效索引范围内原地哈希不是万能的它最适合元素值范围与数组索引存在天然映射关系的问题。比如数组长度为n元素值在[1, n]或[0, n-1]范围内时索引本身就能作为完美的哈希函数。2. 适用场景与使用边界原地哈希最适合解决以下几类问题重复元素检测给定长度为n的数组元素范围在[1, n]之间找出重复出现的数字。经典例题如LeetCode 287寻找重复数。缺失数字查找长度为n的数组包含[0, n]或[1, n1]范围内的数字找出缺失的那个。比如LeetCode 268缺失数字。第一个缺失正数在未排序数组中找到最小的缺失正整数。这是LeetCode 41的经典题目最能体现原地哈希的价值。使用边界需要注意数组元素必须能够映射到有效索引否则需要预处理修改原数组是必要的代价如果数组不可修改则不能使用适用于单次遍历解决问题的场景多次随机访问可能不划算3. 环境准备与前置条件要实践原地哈希算法你只需要基础的编程环境编程语言Python 3.6本文示例使用Python开发工具任意代码编辑器或IDEVS Code、PyCharm等运行环境本地Python解释器或在线编程平台算法基础了解数组操作、时间复杂度分析不需要额外的库或框架原地哈希的核心是算法思维而非工具依赖。4. 原地哈希实现模板原地哈希的基本思路是遍历数组将每个元素放到它应该在的位置上。如果目标位置已经有正确元素说明发现重复如果遍历完成后还有位置不对说明存在缺失。下面是通用的Python实现模板def in_place_hash(nums): n len(nums) # 第一遍遍历将元素放到正确位置 for i in range(n): # 不断交换直到当前位置的元素是合适的或者发现重复 while nums[i] ! i 1: # 假设期望是[1, n]映射到索引[0, n-1] target_index nums[i] - 1 # 如果目标位置已经有正确元素说明nums[i]是重复的 if nums[target_index] nums[i]: break # 交换元素到正确位置 nums[i], nums[target_index] nums[target_index], nums[i] # 第二遍遍历检查哪个位置不符合预期 for i in range(n): if nums[i] ! i 1: return i 1 # 返回缺失的数字 return n 1 # 如果都符合说明缺失的是n1这个模板可以适配多种变体问题关键调整在于映射关系和终止条件。5. 典型例题实战解析5.1 寻找重复数LeetCode 287题目要求给定包含n1个整数的数组nums其数字都在[1, n]范围内假设只有一个重复的数字找出这个重复的数。解题思路利用索引0到n对应数字1到n1遍历数组将每个数字交换到对应的索引位置如果交换时发现目标位置已经是正确数字说明找到重复Python实现def findDuplicate(nums): n len(nums) - 1 # 数字范围是[1, n]数组长度是n1 i 0 while i len(nums): # 如果当前数字已经在正确位置或者当前是00不在[1,n]范围内 if nums[i] i 1 or nums[i] 0: i 1 continue target_index nums[i] - 1 # 如果目标位置已经有相同的数字说明找到重复 if nums[target_index] nums[i]: return nums[i] # 交换到正确位置 nums[i], nums[target_index] nums[target_index], nums[i] return -1 # 理论上不会执行到这里 # 测试用例 test_nums [1, 3, 4, 2, 2] print(findDuplicate(test_nums)) # 输出: 2关键点注意数组长度是n1数字范围是[1, n]交换时要检查目标位置是否已经是正确数字时间复杂度O(n)空间复杂度O(1)5.2 第一个缺失的正数LeetCode 41这是原地哈希最经典的应用场景给你一个未排序的整数数组nums请你找出其中没有出现的最小的正整数。解题思路将数组视为哈希表数字x应该出现在索引x-1的位置遍历数组将每个正整数放到正确位置再次遍历第一个位置不匹配的就是答案Python实现def firstMissingPositive(nums): n len(nums) # 第一遍将正整数放到正确位置 for i in range(n): # 不断交换直到当前元素不在[1, n]范围内或者已经在正确位置 while 1 nums[i] n and nums[nums[i] - 1] ! nums[i]: # 交换到正确位置 correct_index nums[i] - 1 nums[i], nums[correct_index] nums[correct_index], nums[i] # 第二遍查找第一个位置不匹配的 for i in range(n): if nums[i] ! i 1: return i 1 return n 1 # 测试用例 test_cases [ [1, 2, 0], # 期望输出: 3 [3, 4, -1, 1], # 期望输出: 2 [7, 8, 9, 11, 12] # 期望输出: 1 ] for nums in test_cases: print(f输入: {nums}, 输出: {firstMissingPositive(nums[:])}) # 使用[:]避免修改原数组算法分析时间复杂度每个元素最多被交换一次O(n)空间复杂度只使用了常数额外空间O(1)关键技巧while循环确保元素被放到正确位置5.3 缺失数字LeetCode 268给定包含[0, n]中n个数的数组nums找出[0, n]范围内没有出现在数组中的那个数。解题思路数字范围[0, n]正好对应索引[0, n]将每个数字放到对应索引位置遍历检查哪个索引位置的值不等于索引Python实现def missingNumber(nums): n len(nums) # 第一遍将数字放到正确位置 for i in range(n): # 当前位置的数字可能大于n因为缺失一个数所以有一个位置是n while nums[i] ! i and nums[i] n: correct_index nums[i] nums[i], nums[correct_index] nums[correct_index], nums[i] # 第二遍查找缺失的数字 for i in range(n): if nums[i] ! i: return i return n # 如果0到n-1都正确说明缺失的是n # 测试用例 test_cases [ [3, 0, 1], # 期望输出: 2 [0, 1], # 期望输出: 2 [9,6,4,2,3,5,7,0,1] # 期望输出: 8 ] for nums in test_cases: print(f输入: {nums}, 输出: {missingNumber(nums[:])})6. 原地哈希的变体与优化6.1 标记法原地哈希对于不能修改数组元素值的情况可以使用标记法。基本原理是通过正负号来记录某个数字是否出现过。def firstMissingPositiveMark(nums): n len(nums) # 第一遍将非正数标记为n1超出范围 for i in range(n): if nums[i] 0: nums[i] n 1 # 第二遍将出现过的数字对应位置标记为负数 for i in range(n): num abs(nums[i]) if num n: nums[num - 1] -abs(nums[num - 1]) # 第三遍找到第一个正数位置 for i in range(n): if nums[i] 0: return i 1 return n 16.2 循环排序模式循环排序是原地哈希的一种系统化实现特别适合元素范围已知的排序问题。def cyclicSort(nums): n len(nums) i 0 while i n: correct_index nums[i] - 1 # 假设范围是[1, n] # 如果当前元素不在正确位置交换 if nums[i] ! nums[correct_index]: nums[i], nums[correct_index] nums[correct_index], nums[i] else: i 1 return nums # 测试循环排序 test_nums [3, 1, 5, 4, 2] print(排序前:, test_nums) print(排序后:, cyclicSort(test_nums))7. 性能分析与优化技巧7.1 时间复杂度分析原地哈希算法通常包含两个循环第一个循环放置元素到正确位置每个元素最多被交换一次O(n)第二个循环检查结果O(n)总体时间复杂度O(n)7.2 空间复杂度优势与传统哈希表相比的优势哈希表O(n)额外空间原地哈希O(1)额外空间在内存受限环境中优势明显7.3 优化技巧提前终止如果在放置过程中已经发现问题答案可以提前返回。边界处理优化对于超出范围的元素可以在第一轮遍历中集中处理。交换次数优化确保每次交换都让至少一个元素到达正确位置。8. 常见问题与排查方法问题现象可能原因排查方式解决方案无限循环交换逻辑错误元素重复交换打印每次交换的值检查终止条件确保不会重复处理同一元素数组越界映射关系错误索引计算超出范围检查索引计算逻辑添加边界检查确保索引在[0, n-1]范围内错误结果元素范围假设错误验证输入数据范围明确问题要求调整映射关系修改原数组算法特性如此如果需要保留原数组先复制数组在副本上操作8.1 典型错误示例# 错误示例缺少边界检查 def wrongInPlaceHash(nums): n len(nums) for i in range(n): # 可能越界如果nums[i]很大 while nums[i] ! i 1: target_index nums[i] - 1 # 可能越界 nums[i], nums[target_index] nums[target_index], nums[i] # ... 后续检查逻辑修正方法def correctInPlaceHash(nums): n len(nums) for i in range(n): # 添加范围检查 while 1 nums[i] n and nums[i] ! i 1: target_index nums[i] - 1 # 避免重复交换 if nums[target_index] ! nums[i]: nums[i], nums[target_index] nums[target_index], nums[i] else: break # ... 后续检查逻辑9. 最佳实践与使用建议9.1 适用场景判断在遇到数组问题时先问自己这几个问题元素范围是否已知如果数字范围在[1, n]或[0, n-1]之间优先考虑原地哈希。是否允许修改原数组原地哈希必须修改数组如果要求保持原数组不变需要先复制。空间限制是否严格如果要求O(1)空间复杂度原地哈希是理想选择。9.2 编码实践建议模板化开发掌握基本模板根据具体问题调整映射关系。测试用例设计覆盖边界情况如空数组、单个元素、完全有序、完全逆序等。逐步验证先在小规模数据上验证逻辑正确性再处理大规模数据。9.3 面试应用技巧沟通思路先说明选择原地哈希的原因空间复杂度优势。手写代码熟练掌握模板能够快速写出无bug的实现。复杂度分析清晰说明时间复杂度和空间复杂度。原地哈希是面试中常见的高频考点特别是LeetCode 41第一个缺失的正数和287寻找重复数。掌握这个技巧能在很多数组相关问题中给出最优解。10. 总结与下一步原地哈希的核心价值在于用索引本身作为哈希函数在O(1)空间内解决数组统计问题。最关键的是识别适用场景——当元素范围与索引范围存在天然映射时这就是最佳选择。建议从LeetCode 41开始练习这是最经典的原地哈希应用题。掌握后可以扩展到268、287、448等相似问题。在实际编码中注意边界处理和终止条件避免无限循环。下一步可以学习更多空间换时间的技巧比如位运算、快慢指针等这些方法与原地哈希结合使用能解决更复杂的数组问题。

相关新闻

2026/9/7 12:09:35

加扰与解扰原理及工程实践:从LFSR到同步恢复的完整指南

简介:加扰与解扰是数字通信中改善信号质量与数据安全的关键环节。这一资源面向通信工程与FPGA开发学习者或相关工程师,聚焦基于VHDL的加解扰算法设计、Modelsim功能仿真及硬件板卡验证。压缩包共132个文件,约1.37MB,以vhd源码文件…

2026/9/7 12:09:35

AES密钥查找工具原理与内存转储分析实战

简介:这份开源工具面向安全分析与逆向工程场景,可帮助安全研究员、CTF选手在运行进程的内存中定位AES密钥,支持128位、192位与256位密钥。工具基于C实现,压缩包共10个文件,以.h头文件、.cpp源码及Visual Studio工程文件…

2026/9/7 12:59:43

镜像处理技术:从基础算法到数字艺术创作实践

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 12:59:43

STM32温控风扇实战:从ADC采集到PWM控制的完整嵌入式项目解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 12:59:43

Godot引擎实战:从架构解析到2D弹幕游戏开发与常见坑

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 12:59:43

GPS+IMU组合导航Matlab开源仿真:卡尔曼滤波融合与调参实战

简介:面向惯性导航与组合导航方向的开发者、学生及研究人员,这套MATLAB开源程序基于NaveGo框架,聚焦GPS与IMU数据融合,重点展示扩展卡尔曼滤波的实际落地方式。压缩包共66个文件,核心为56个m源码脚本,搭配m…

2026/9/7 12:59:43

服务器运维实战:高温散热、迁移选型与高频故障排查指南

最近一周,服务器相关的话题再一次霸占了技术圈的热搜列表。天气热起来了,有人在群里晒机房温度告警:空调一坏,几十台服务器瞬间“泡汤”;有运营多年的老游戏突然宣布“重启”,玩家在欢呼,运维却…

2026/9/7 12:54:43

Java字符排序器Collator详解:从中文拼音到自定义规则

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/7 0:47:43

超人会飞不算本事:系统稳定依赖清晰规则与边界设计

开头先不绕弯子。“#斯坦李吐槽dc 所以超人是无缘无故会飞的嘛哈哈哈哈哈哈哈锤哥真是技术人才啊!#雷神 #复联”这类调侃式短标题,第一波冲击力在于它把两个宇宙的角色塞进同一个吐槽箱里,但细想一下就能发现,它真正碰到的根本不是…

2026/9/7 0:14:19

超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论

把“蜘蛛侠 vs 超人”放在 CSDN 上聊,可能很多人第一反应是走错片场了。但如果把这两个角色看成“两个持续运营了 80 多年的文化产品”,你会发现,这场比较本质上是两个不同 IP 策略的长期结果对比:超人赢在定义了整个超级英雄题材…

2026/9/7 0:14:17

基于CNN的调制信号识别:MATLAB实现时频图分类实战

简介:本资源是一套面向通信工程与信号处理方向学习者、研究者的深度学习实践方案,聚焦调制信号自动检测与识别这一典型无线通信任务,解决传统方法依赖人工特征、低信噪比下性能下降等痛点。压缩包共12个文件(10.73MB)&…

2026/9/7 0:03:36

基于YOLOv8和PyQt5的麦穗稻穗检测识别系统设计与实现

这次我们来看一个把目标检测算法和桌面端工具结合得很典型的项目:基于 YOLOv8 PyQt5 的麦穗稻穗检测识别系统。这个项目本身不是新概念,但它的价值在于落地形态很完整。YOLOv8 负责核心的麦穗稻穗目标检测,PyQt5 负责提供可视化的桌面交互界…

2026/9/7 0:03:36

UL 1642锂电池安全标准全解析:测试项目、认证流程与避坑指南

简介:UL 1642是锂电池安全领域的重要规范,本中文版资源适合锂电池制造商、检测机构工程师及产品认证相关人员阅读,用于理解电池在设计与制造层面的安全要求、测试方法与合规要点。资源共1个PDF文件,压缩包大小834KB,便…

2026/9/7 0:03:36

BS EN 13814-1-2019游乐设施安全标准:设计与制造核心要点解析

简介:BS EN 13814-1:2019是英国采纳欧洲标准EN 13814-1:2019的正式版本,由BSI标准出版,重点规定游乐设施和游乐设备在设计与制造环节的安全准则,与BS EN 13814-2:2019、BS EN 13814-3:2019共同取代旧版BS EN 13814:2004。该标准面…

2026/9/6 11:40:10

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/6 19:33:50

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/6 10:19:40

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…