【Hot 100 刷题计划】 LeetCode 41. 缺失的第一个正数 | C++ 原地哈希题解

LeetCode 41. 缺失的第一个正数 | C++ 哈希表基础解法

📌 题目描述

题目级别:困难 (Hard)

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

  • 示例 1:

    输入:nums = [1,2,0]

    输出:3

    解释:范围 1,2 中的数字都在数组中。

  • 示例 2:

    输入:nums = [3,4,-1,1]

    输出:2

    解释:1 在数组中,但 2 没有。


🚀 解法一:常规哈希表 (直觉解法,空间 O(N))

寻找"缺失的数字",最符合人类直觉的方法就是:把见过的数字都记在小本本上,然后从 1 开始挨个往上数,看看谁没被记过。

在代码实现中,这个"小本本"最合适的数据结构就是哈希集合(unordered_set)。

  1. 遍历原数组,把所有的正整数都扔进 unordered_set 中记录下来(自动去重且查找时间为 O(1)O(1)O(1))。
  2. 初始化一个目标值 target = 1,不断递增,去哈希表里查。
  3. 查不到的那个数字,就是缺失的第一个正数!

💻 C++ 代码实现

cpp 复制代码
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        // 使用哈希集合记录所有出现过的正整数
        unordered_set<int> seen;
        for (int num : nums) {
            if (num > 0) {
                seen.insert(num);
            }
        }

        // 从 1 开始逐个检查谁没出现过
        int target = 1;
        while (true) {
            // 如果在哈希表中找不到 target,说明这就是缺失的最小正数
            if (seen.find(target) == seen.end()) {
                return target;
            }
            target++;
        }
    }
};

🏆 解法二:原地哈希 / 循环排序 (面试满分终极解,空间 O(1))

题目给出了极其严苛的条件:时间 O(N)O(N)O(N) 且空间 O(1)O(1)O(1)。这意味着我们不能开辟新的哈希表,只能把原数组本身当作哈希表来用

对于一个长度为 NNN 的数组,它里面能装下的"连续正数"最多也就是 1, 2, 3... N

如果数组里装满了 1N,那缺失的正数就是 N + 1;如果不满,缺失的正数一定在 [1, N] 之间。

因此,我们可以制定一个规矩:数字 i 必须老老实实呆在索引 i - 1 的位置上(即数字 1 放在索引 0,数字 2 放在索引 1...)。这就好比"一个萝卜一个坑"。

操作步骤:

  1. 数字归位 :遍历数组,只要当前萝卜(数字)是个有效正数,且没呆在正确的坑位上,我们就把它交换到它该去的地方。换过来的新萝卜如果也不对,就继续换。
  2. 查岗核对:等所有萝卜都归位了,我们再从头巡查一遍。如果发现哪个坑里装的不是正确的萝卜,那个坑对应的数字就是缺失的!

💻 C++ 代码实现

cpp 复制代码
class Solution {
public:
    int firstMissingPositive(vector<int>& nums) {
        int n = nums.size();
        
        // 第一阶段:将所有在 [1, n] 范围内的萝卜归位
        for (int i = 0; i < n; i++) {
            // 只要当前数字是有效正数,且不在正确的坑位上,就不断进行交换
            while (nums[i] > 0 && nums[i] <= n && nums[i] != nums[nums[i] - 1]) {
                // 将 nums[i] 送回到属于它的索引位置 (nums[i] - 1)
                swap(nums[i], nums[nums[i] - 1]);
            }
        }

        // 第二阶段:查岗,寻找第一个坑位不对的数字
        for (int i = 0; i < n; i++) {
            // 索引 i 应该对应数字 i + 1
            if (nums[i] != i + 1) {
                return i + 1; // 找到了缺失的最小正数
            }
        }
        
        // 如果 1 到 n 都整整齐齐排好了,那缺失的就是 n + 1
        return n + 1;
    }
};
相关推荐
rannn_1119 小时前
【力扣hot100】链表专题|160、206、234、141、142
java·算法·leetcode·链表·面试·开发
不会代码的小猴9 小时前
21. 泛型编程上
开发语言·c++·笔记·算法
营养充电站10 小时前
VS Code Git 工作树:解锁多分支并行开发的高效体验
leetcode·决策树·逻辑回归·散列表·模拟退火算法
青瓦梦滋10 小时前
传输层UDP/TCP协议
linux·网络·c++·网络协议·tcp/ip·udp
一只旭宝10 小时前
细讲C加加【9】C++ std::function与std::bind详解|仿函数、绑定器、类成员绑定、占位符、成员偏移指针
开发语言·c++·算法
良木林11 小时前
子串 - LeetCode hot 100
算法·leetcode·职场和发展
蒸蒸yyyyzwd11 小时前
秋招不熟悉力扣学习笔记1
笔记·学习·leetcode
Lhan.zzZ12 小时前
在 Visual Studio 2022 中打造可扩展的动态链接库模块:从零搭建到原理解析
开发语言·c++·visual studio
zh路西法14 小时前
【3D SLAM源码解读系列】(二)Small_gicp——5 个积木搭出最优点云配准
c++·pcl·icp·fastgicp·smallgicp·gicp
fpcc16 小时前
ubuntu26环境下的开发环境安装处理
c++·并行编程