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

普通数组——缺失的第一个正数

给你一个未排序的整数数组 nums ,请你找出其中没有出现的最小的正整数。

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案
这道题是哈希思想+原地哈希的经典面试题,要求O(n)时间+O(1)空间,核心思路是把数组本身当作哈希表,让每个数字num放到下标num-1的位置。

核心原理
1.缺失的最小正整数,一定在[1,n+1]范围内

  • 例:[1,2,3]->缺失4
  • 例:[3,4,-1,1] -> 缺失2
    2.遍历数组,把每个正数x放到下标x-1的位置
    3.再次遍历,第一个下标i不满足nums[i] = i+1 , i+1就是答案

代码逐行解释
第一步:原地调整数字位置
while (nums[i] >= 1 && nums[i] <= n && nums[nums[i]-1] != nums[i])
swap(nums[i], nums[nums[i]-1]);

  • 只处理1~n之间的正数(超出范围的数不用管)
  • 把数字x交换到下标x-1
  • 循环交换,直到当前位置数字正确/超出范围

示例:[3,4,-1,1]
调整后 → [1, -1, 3, 4]

第二步:查找缺失数字
遍历数组,第一个数字!=下标+1的位置,就是答案:

  • [1, -1, 3, 4]
  • 下标 0:1=0+1 ✔️
  • 下标 1:-1≠1+1 ❌ → 返回 2

特殊情况
数组是 [1,2,3] → 全部匹配 → 返回 4(n+1)

复杂度
时间复杂度:O (n)(每个数字最多交换一次)
空间复杂度:O (1)(仅用临时变量,无额外数组)

总结
核心:用数组本身做哈希,让nums[i]=i+1
两步走:调整数字位置->遍历找答案
完美满足:O (n) 时间 + O (1) 空间,面试高频最优解

注意
while (nums[i] >= 1 && nums[i] <= n && nums[nums[i]-1] != nums[i])
swap(nums[i], nums[nums[i]-1]);这里的while绝对不能换成if,必须用while

解释:
因为一次交换可能不够!,交换后,新换到i位置的数字,可能仍然需要再次交换
if朱慧判断/交换1次
while会一直交换,直到这个位置数字正确为止

举个例子(彻底懂)
数组:[3, 4, -1, 1]
n = 4
我们看 **i = 0 **这个位置:
nums[0] = 3

用 while(正确)

  1. 3 应该放到下标 2
    交换 → nums = [-1, 4, 3, 1]
  2. 现在 nums[0] = -1
    不在 1~4 范围内 →** 停止**
    位置 0 处理完毕

用 if(错误)

  1. 3 应该放到下标 2
    交换 → nums = [-1, 4, 3, 1]
  2. if 执行完直接退出,不再检查新数字
  3. 循环继续到 i=1
    nums[1] = 4 → 换到下标 3
    交换 → [-1,1,3,4]
  4. 现在 nums[1] = 1,if 又只交换一次
    1 应该放到下标 0!
    但 if 不会再处理了!
    最终数组变成:
    [-1, 1, 3, 4]
    数字 1 永远放不到正确位置!答案错误!

一句话总结
if = 只交换 1 次
while = 交换到正确为止
我们需要的是:
把当前位置处理到完全正确,再去下一个位置
所以必须用while!

完整代码实现如下
#include
#include
using namespace std;

int firstMissingPositive(vector& nums) {

int n = nums.size();// 第一步:原地哈希,把每个正数放到正确位置
for (int i = 0; i < n; ++i) {// 只有当数字在 [1,n] 范围内,且不在正确位置时,才交换while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {swap(nums[i], nums[nums[i] - 1]);}
}// 第二步:遍历找第一个不匹配的位置
for (int i = 0; i < n; ++i) {if (nums[i] != i + 1) {return i + 1;}
}// 数组是 [1,2,...,n],返回 n+1
return n + 1;

}

// 测试主函数
int main() {

vector<int> nums1 = {3, 4, -1, 1};
cout << firstMissingPositive(nums1) << endl;  // 输出 2vector<int> nums2 = {1, 2, 0};
cout << firstMissingPositive(nums2) << endl;  // 输出 3vector<int> nums3 = {7, 8, 9, 11};
cout << firstMissingPositive(nums3) << endl;  // 输出 1
return 0;

}

http://www.jsqmd.com/news/557175/

相关文章:

  • 【JAVA】Spring3.x中的swagger配置基础教程
  • 文明狭义论与广义论
  • QWEN-AUDIO性能优化指南:让语音合成速度提升50%的实用技巧
  • Easysearch ZSTD 基准测试:高压缩率下实现近 5 倍查询吞吐
  • 3分钟搞定全网音乐歌词下载与管理的终极指南:网易云音乐与QQ音乐歌词批量处理
  • three-csg-ts:三维布尔运算的优雅解决方案
  • 保姆级避坑指南:在Ubuntu 22.04上搞定奥比中光AstraPro深度相机与ROS2 Humble的驱动配置
  • WPF颜色转换器实战:如何用ConverterParameter动态切换UI主题色(附完整代码)
  • Vue项目里图片403报错?试试在index.html里加这行meta标签
  • 告别轮询延时!在RTOS里优雅处理AT24C02的Write Cycle等待
  • 2026年铝方通铝扣板应用白皮书家居吊顶篇:青岛铝方通格栅、青岛铝方通隔断、青岛集成吊顶铝扣板、青岛U型铝方通选择指南 - 优质品牌商家
  • 避坑指南:Android虚拟摄像头开发中JPG转YUV的SELinux权限与符号链接问题全解析
  • 记一次SQL server2008 数据库事务日志已满,导致程序崩溃排查过程
  • 2026年工业防火门市场测评:五大实力厂商深度解析与选型指南 - 2026年企业推荐榜
  • 突破平台限制:开源工具WorkshopDL实现Steam创意工坊内容自由获取
  • EfficientNet实战:如何在移动端部署B0-B7模型(附显存优化技巧)
  • LlamaIndex中文文档全解析:从安装到实战RAG系统的保姆级指南
  • Outline数据迁移架构深度解析:5大策略构建企业级知识库无缝迁移方案
  • 从单任务到持续学习:AI原生应用的演进之路
  • 通达信数据接口实战指南:用MOOTDX构建量化投资数据引擎
  • OpenClaw+GLM-4.7-Flash内容创作实测:从选题到发布的自动化链路
  • 4大维度重塑数据库实验流程:让命令行成为数据库管理的瑞士军刀
  • 3大突破!LxgwWenKai如何解决嵌入式系统中文显示难题?
  • Iono系列工业PLC模块:Arduino生态的工业级演进
  • 航拍小目标检测入门必看:YOLOv8 VisDrone实战第一阶段,基线mAP从32%提升至58%
  • Python内存修复黄金法则(CPython内存管理内核级解析)
  • 新手也能看懂的LMXCMS 1.4代码审计:从MVC架构入手,一步步挖出两个后台RCE漏洞
  • Vita3K模拟器完整入门指南:快速解决常见问题并优化游戏体验
  • 从滞后补偿器到PI控制:原理、设计与系统性能优化
  • 学习C#调用Microsoft.ML.OnnxRuntime+OpenCvSharp+YOLO26进行目标检测的基本用法