【忍者算法】从图书馆编目到数组搜索:探索缺失的第一个正整数|LeetCode 41 缺失的第一个正整数

从图书馆编目到数组搜索:探索缺失的第一个正整数

生活中的算法

想象你是一位图书馆管理员,正在整理一排连续编号的图书。这些书应该从1号开始按顺序排列,但是有些编号的书不见了。你的任务是找出第一个缺失的编号。这就像是在做点名,发现第一个没来上课的同学。

这个场景在生活中很常见。比如:

  • 餐厅服务员查看哪个桌号是第一个空位
  • 停车场管理员寻找第一个空闲的车位号
  • 学校给新生分配第一个未使用的学号
  • 医院为病人安排第一个可用的就诊序号

问题描述

LeetCode第41题"缺失的第一个正整数"是这样描述的:给你一个未排序的整数数组 nums,请你找出其中没有出现的最小的正整数。请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

例如:

输入:nums = [3,4,-1,1]
输出:2
解释:数组中有1,3,4,所以第一个缺失的正整数是2。

输入:nums = [7,8,9,11,12]
输出:1
解释:数组中没有1,所以缺失的第一个正整数是1。

最直观的解法:排序后遍历

就像整理图书时,先把所有的书按编号排好序,然后从1开始检查,看哪个编号最先空缺。

让我们用一个简单的例子来理解:

原数组:[3,1,4,-1]
1. 先排序(只考虑正数):[1,3,4]
2. 从1开始检查:
   - 1存在
   - 2不存在,找到答案!

优化解法:原地标记

仔细思考会发现一个关键点:如果数组长度为n,那么答案一定在[1, n+1]范围内。就像有10个学生的班级,第一个缺席的学号最大不会超过11。

我们可以把数组本身作为标记板,把每个数放到它应该在的位置上(就像把每本书放到对应的编号位置)。

原地标记的原理

  1. 把每个在[1,n]范围内的数x放到索引x-1的位置
  2. 再次遍历,第一个不在对应位置的数就指示了缺失的最小正整数
    这就像是:
  • 先把每本书放到它编号对应的书架位置
  • 然后从1号位置开始检查,找到第一个空位置

示例演示

用nums = [3,4,-1,1]来说明:

1. 开始移动元素:
   [3,4,-1,1] 把3放到索引2
   [3,4,3,1] 把4放到索引3
   [3,1,3,4] 把1放到索引0
   [1,3,3,4]

2. 再次遍历,检查每个位置:
   位置0:期望1,实际1,正确
   位置1:期望2,实际3,找到答案2

Java代码实现

public int firstMissingPositive(int[] nums) {
    int n = nums.length;
    
    // 第一遍遍历:把每个数放到它应该在的位置
    for (int i = 0; i < n; i++) {
        // 当前数在有效范围内,且不在正确位置上
        while (nums[i] > 0 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {
            // 交换到正确位置
            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;
        }
    }
    
    // 如果都正确,返回n+1
    return n + 1;
}

解法比较

让我们比较这两种方法:

排序后遍历:

  • 时间复杂度:O(nlogn)
  • 空间复杂度:O(1)
  • 优点:思路直观,易于理解
  • 缺点:不满足时间复杂度要求

原地标记:

  • 时间复杂度:O(n)
  • 空间复杂度:O(1)
  • 优点:满足所有要求,不需要额外空间
  • 缺点:实现略微复杂,需要仔细处理边界情况

解题技巧总结

这道题给我们的启示:

  1. 思考数据的取值范围很重要
  2. 有时候可以把数组本身当作标记数组使用
  3. 位置和值的对应关系常常能带来灵感
  4. 不要害怕修改原数组,有时这是提高效率的关键

类似的问题还有:

  • 数组中重复的数字
  • 找到所有数组中消失的数字
  • 寻找重复数

小结

通过缺失的第一个正整数这道题,我们学会了一个重要的思维方式:有时候看似需要额外空间的问题,可以通过巧妙利用输入数组本身来解决。这种思维不仅在这道题中有用,在处理其他需要标记或计数的问题时也很有启发。记住,当遇到需要找寻特定范围内缺失数字的问题时,考虑能否利用数组本身来存储信息!


作者:忍者算法
公众号:忍者算法

完整题解项目 :https://github.com/ninjaAlgorithm/LeetCode-Solutions-Hot-100

posted @   忍者算法  阅读(138)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 本地部署 DeepSeek:小白也能轻松搞定!
· 传国玉玺易主,ai.com竟然跳转到国产AI
· 自己如何在本地电脑从零搭建DeepSeek!手把手教学,快来看看! (建议收藏)
· 我们是如何解决abp身上的几个痛点
· 如何基于DeepSeek开展AI项目
点击右上角即可分享
微信分享提示