LeetCode 41.缺失的第一个正数|Hard题O(n)+O(1)最优解法深度解析

一、题目介绍

题目描述

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

进阶要求:算法的时间复杂度应当为 O(n),并且只能使用常数级别的额外空间。

示例展示

1.输入:1,2,0,输出:3

解析:1、2 均存在,缺失最小正数为 3

2.输入:3,4,-1,1,输出:2

解析:1 存在,2 未出现,为答案

3.输入:7,8,9,11,12,输出:1

解析:最小正整数 1 直接缺失

前置关键结论

数组长度为 n 时,缺失的最小正整数只存在两种可能:

1.1∼n 之间某个数字空缺(答案在区间内)

2.1∼n 全部完整存在(答案为 n+1)

负数、大于 n 的数字不会成为最终答案,可以直接忽略处理。

二、错误写法复盘(你的原始代码问题拆解)

原始代码核心错误

1.包含判断函数逻辑颠倒

自定义IsContain函数,匹配到目标值返回false,无匹配返回true,和 "判断元素是否存在" 逻辑完全相反,基础功能失效。

java 复制代码
// 错误源码
public boolean IsContain(int[] nums,int a){
    for (int i = 0; i < nums.length; i++) {
        if (nums[i]==a) {
            return false; // 找到元素本该true,写反
        }
    }
    return true;
}

2.整体遍历逻辑设计缺陷

依靠numsi+1向后寻找空缺值,无法识别1缺失的场景,对负数、超大数值过滤失效,边界用例全部翻车。

3.时间复杂度不达标

双重循环遍历数组做存在性判断,时间复杂度 O(n2),数组长度达到105时直接超时,不满足题目硬性要求。

4.代码冗余:flag 标记、result 更新逻辑混乱,无清晰判断逻辑。

暴力修正版(仅用于理解逻辑,不推荐提交)

修复判断函数后可以正常算出结果,但无法通过大数据量用例:

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) {
        int result = 1;
        // 先校验1是否缺失
        if (!isContain(nums, result)) {
            return result;
        }
        // 依次向后查找空缺正整数
        while (true) {
            result++;
            if (!isContain(nums, result)) {
                return result;
            }
        }
    }
    // 修正:元素存在返回true,不存在返回false
    public boolean isContain(int[] nums, int a) {
        for (int num : nums) {
            if (num == a) return true;
        }
        return false;
    }
}

三、最优解法:原地哈希(置换法,满足O(n)时间 +O(1)空间)

核心思想

利用数组下标作为哈希映射:

对于合法正整数 x(1≤x≤n),将它交换到数组下标 x−1 的位置。

完成一轮置换后,数组理想形态为:nums0=1、nums1=2、nums2=3......

再次遍历数组,第一个 numsi != i+1 的位置,i+1 就是缺失的最小正数;全部匹配则返回 n+1。

详细执行步骤

1.遍历数组做原地置换

只处理1~n范围内的数字,循环交换将数字放到对应下标;增加重复值判断,避免死循环。

2.线性扫描查找空缺位

从下标 0 开始对比下标与数值的对应关系,第一个错位位置即为答案。

3.全连续兜底

若数组所有位置都满足numsi=i+1,说明 1~n 全部存在,返回n+1。

Java AC 完整代码

java 复制代码
class Solution {
    public int firstMissingPositive(int[] nums) {
        int n = nums.length;
        // 原地哈希置换
        for (int i = 0; i < n; i++) {
            // 仅处理合法正整数,防止数组越界+重复元素死循环
            while (nums[i] >= 1 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {
                int temp = nums[nums[i] - 1];
                nums[nums[i] - 1] = nums[i];
                nums[i] = temp;
            }
        }
        // 寻找第一个不匹配的下标
        for (int i = 0; i < n; i++) {
            if (nums[i] != i + 1) {
                return i + 1;
            }
        }
        // 1~n全部齐全
        return n + 1;
    }
}

案例推演演示

案例:nums = 3,4,-1,1,数组长度 n=4

1.置换过程

i=0,nums 0=3,交换到下标 2 → -1,4,3,1

i=1,nums 1=4,交换到下标 3 → -1,1,3,4,继续交换 nums 1=1 到下标 0 → 1,-1,3,4

i=2,nums 2=3,位置正确无需交换

i=3,nums 3=4,位置正确无需交换

最终数组:1,-1,3,4

2.遍历校验

i=0:1=0+1 ✔️

i=1:-1≠2,直接返回结果 2,和样例输出一致。

四、复杂度分析

•时间复杂度 O(n):每个元素最多被交换 1 次,两次线性遍历数组,无嵌套循环

•空间复杂度 O(1):全程原地修改数组,仅使用临时交换变量,无额外数组、集合开辟

五、易错踩坑总结

1.置换循环必须增加numsnums\[i-1] != numsi判断,数组存在重复数字时会陷入无限循环;
2.负数、大于数组长度的数字直接跳过置换,不会作为答案,无需处理;
3.不要忽略全连续场景,例如1,2,3最终需要返回 4;
4.暴力枚举解法逻辑简单,但大数据用例直接超时,面试必须掌握原地哈希解法。

六、适用场景拓展

该原地哈希思想可以拓展到:

1.寻找数组重复元素
2.寻找缺失元素类线性空间限制题目

是算法面试高频的空间优化经典技巧。

相关推荐
Tongzhi20261 小时前
从部署到运维:通芝科技无感考勤一体机的全流程效率解析
运维·数据结构·科技·算法·贪心算法
Wang's Blog1 小时前
AI Agent白手起家30: 动态示例选择器之根据长度选择 Few Shot 示例
算法
markinmarkin3 小时前
Spring 中Bean 的作用域有哪些?
java·后端·spring
oyguyteggytrrwwwrt3 小时前
自制交叉线路识别算法
算法
手写码匠3 小时前
华为云Flexus+DeepSeek征文|Dify 多智能体协同编排实战:R1 规划 + V3 执行,构建企业 Agent 团队
人工智能·深度学习·算法·aigc
山荷枝4 小时前
Java学习第十天
java·学习
晓天衡宇•评测社区4 小时前
FSR-Bench 前沿科学推理榜单发布:GPT-5.5 居首,“会推理”未必“能答对”
算法
matlabgoodboy4 小时前
计算机毕设代做|Java Python Matlab APP 全套开发设计
java·python·课程设计
程序喵大人5 小时前
【C++进阶】STL算法与函数对象 - 02 sort为什么需要随机访问迭代器
开发语言·c++·算法