缺失的第一个正数(LeetCode 41)

文章目录

  • 1.问题描述
  • 2.难度等级
  • 3.热门指数
  • 4.解题思路
    • 4.1 暴力
    • 4.2 排序
    • 4.3 哈希表
    • 4.4 空间复杂度为 O(1) 的哈希表
    • 4.5 置换
  • 参考文献

1.问题描述

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

请你实现时间复杂度为 O(n) 并且只使用常数级别额外空间的解决方案。

示例 1:

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

示例 2:

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

示例 3:

输入:nums = [7,8,9,11,12]
输出:1

提示:

1 <= nums.length <= 5 * 10^5
-2^31 <= nums[i] <= 2^31 - 1

2.难度等级

Hard。

这道题很简单,但是题目的要求是 O(n) 的时间复杂度和 O(1) 空间复杂度,所以难度上升到了 Hard。

3.热门指数

★★★★☆

4.解题思路

4.1 暴力

最容易想到的做法是暴力法。

我们可以从 1 开始依次枚举正整数,并遍历数组,判断其是否在数组中。

时间复杂度:

如果数组的长度为 n,时间复杂度为 O(n^2)。

所以该算法时间复杂度不满足题目要求。

空间复杂度:

O(1)。只需要常数级别的空间存储若干变量。

下面以 Golang 为例给出实现。

func firstMissingPositive(nums []int) int {p := 1for {exist := falsefor i := 0; i < len(nums); i++ {if nums[i] == p {p++exist = truebreak}}if !exist {return p}}
}

4.2 排序

我们可以对数组排序,然后遍历数组,找到第一个不在 nums 中的正整数。

时间复杂度:

O(nlogn),排序一般时间复杂度为 O(nlogn),然后遍历排序后的数组,最坏时间复杂度为 O(n),所以总的时间复杂度为 O(nlogn)。

所以该算法时间复杂度不满足题目要求。

空间复杂度:

O(1)。只需要常数级别的空间存储若干变量。

下面以 Golang 为例给出实现。

func firstMissingPositive(nums []int) int {slices.Sort(nums)p := 1for i := 0; i < len(nums); i++ {if nums[i] == p {p++}}return p
}

4.3 哈希表

我们可以将数组所有的数放入哈希表,随后从 1 开始依次枚举正整数,并判断其是否在哈希表中。

时间复杂度:

如果数组的长度为 n,那么时间复杂度为 O(n)。

空间复杂度:

需要哈希表存储数组所有元素,空间复杂度为 O(n)。

所以该算法空间复杂度不满足题目要求。

下面以 Golang 为例给出实现。

func firstMissingPositive(nums []int) int {m := make(map[int]struct{}, len(nums))for _, v := range nums {m[v] = struct{}{}}for p := 1; ; p++ {if _, ok := m[p]; !ok {return p}}
}

4.4 空间复杂度为 O(1) 的哈希表

题目要求空间复杂度需要 O(1),所以不能使用额外的哈希表存储数组中的元素。

我们将目光放到数组本身。

实际上,对于一个长度为 n 的数组,其中没有出现的最小正整数只能在 [1,n+1] 中。我们可以利用数组本身,将数组中大于等于 1 小于等于 n 的正整数,在数组中打上标记。

打完标记后,遍历数组,如果下标 i 没有被打上标记,那么 i+1 就是数组中缺失的第一个正整数。

如果数组所有下标均被打上标记,那么 n+1 就是数组中缺失的第一个正整数。

如何给数组下标打上标记呢?

由于我们只在意 [1,n] 中的数,因此我们可以先对数组进行遍历,把不在 [1,n] 范围内的数修改成任意一个大于 n 的数(例如 n+1)。这样一来,数组中的所有数就都是正数了,因此我们就可以将「标记」表示为「负号」。

算法的流程如下:

  • 我们将数组中所有小于等于 0 的数修改为 n+1。

  • 我们遍历数组中的每一个数 x,它可能已经被打了标记,因此原本对应的数为 |x|,其中 || 为绝对值符号。如果 |x|∈[1,n],那么我们给数组中的第 |x|−1 位置的数添加一个负号。注意如果它已经有负号,不需要重复添加。

  • 在遍历完成之后,如果数组中的每一个数都是负数,那么答案是 n+1,否则答案是第一个正数的下标加 1。

在这里插入图片描述

时间复杂度:

三次遍历数组,第一次遍历将数组中所有非正数变成 n+1。第二次遍历给数组下标打标记。第三次遍历获取没有打标记的下标。所以时间复杂度是 O(n),满足题目要求。

空间复杂度:

没有使用额外的存储空间,所以是 O(1),满足题目要求。

下面以 Golang 为例给出实现。

func firstMissingPositive(nums []int) int {// 将数组中所有小于等于 000 的数修改为 n+1。for i, v := range nums {if v <= 0 {nums[i] = len(nums) + 1}}// 给数组下标打标记。for _, v := range nums {abs := int(math.Abs(float64(v)))if abs >= 1 && abs <= len(nums) {if nums[abs-1] > 0 {nums[abs-1] = -nums[abs-1]}}}// 遍历数组,寻找未打标记的下标。for i, v := range nums {if v > 0 {return i + 1}}return len(nums) + 1
}

4.5 置换

除了打标记以外,我们还可以使用置换的方法,将给定的数组「恢复」成下面的形式:

如果数组中包含 x∈[1,n],那么恢复后,数组的第 x−1 个元素为 x。

在恢复后,数组应当有 [1, 2, …, n] 的形式,但其中有若干个位置上的数是错误的,每一个错误的位置就代表了一个缺失的正数。以题目中的示例二 [3, 4, -1, 1] 为例,恢复后的数组应当为 [1, -1, 3, 4],我们就可以知道缺失的数为 2。

那么我们如何将数组进行恢复呢?我们可以对数组进行一次遍历,对于遍历到的数 x=nums[i],如果 x∈[1,n],我们需要将 x 放在数组中的 x−1 的位置,因此交换 nums[i] 和 nums[x−1],这样 x 就出现在了正确的位置。

在完成交换后,新的 nums[i] 可能还在 [1,n] 的范围内,我们需要继续进行交换操作,直到 x∉[1,n]。

注意到上面的方法可能会陷入死循环。如果 nums[i] 恰好与 nums[x−1] 相等,那么就会无限交换下去。此时我们有 nums[i]=x=nums[x−1],说明 x 已经出现在了正确的位置。因此我们可以跳出循环,开始遍历下一个数。

时间复杂度:

由于每次的交换操作都会使得某一个数交换到正确的位置,因此交换的次数最多为 n,整个方法的时间复杂度为 O(n)。

空间复杂度:

没有使用额外的存储空间,所以是 O(1)。

下面以 Golang 为例给出实现。

func firstMissingPositive(nums []int) int {n := len(nums)for i := range nums {for nums[i] > 0 && nums[i] <= n && nums[i] == nums[nums[i]-1] {nums[i], nums[nums[i]-1] = nums[nums[i]-1], nums[i]}}for i, v := range nums {if v != i+1 {return i + 1}}return len(nums) + 1
}

参考文献

41. 缺失的第一个正数 - LeetCode

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://xiahunao.cn/news/2661303.html

如若内容造成侵权/违法违规/事实不符,请联系瞎胡闹网进行投诉反馈,一经查实,立即删除!

相关文章

网络MAC

网口框架 关键字 MAC&#xff1a; media access controller RMI: reduced media interface SMI&#xff1a;serial media interface N/A: Not applicable 全双工 & 半双工 3.1、在全双工模式下&#xff0c;8网根线都要分别接到水晶头相应的线序位置上&#xff1b; 3.2在…

数据之光:乡镇企业的发展利器——数据可视化

数据可视化是一项强大的工具&#xff0c;它不仅在大型企业中发挥关键作用&#xff0c;而且在乡镇企业中也能作出显著贡献。那么&#xff0c;数据可视化究竟能为乡镇企业做出什么样的贡献呢&#xff1f; 首先&#xff0c;数据可视化为乡镇企业提供了更清晰的业务洞察。通过将庞大…

Linux性能优化全景指南

Part1 Linux性能优化 1、性能优化性能指标 高并发和响应快对应着性能优化的两个核心指标&#xff1a;吞吐和延时 应用负载角度&#xff1a;直接影响了产品终端的用户体验系统资源角度&#xff1a;资源使用率、饱和度等 性能问题的本质就是系统资源已经到达瓶颈&#xff0c;但…

swing快速入门(三十一)文件选择器

注释很详细&#xff0c;直接上代码 上一篇 新增内容 1.菜单项按键响应 2. 文件选择器对话框用法 3.绘画板用法 package swing21_30;import javax.imageio.ImageIO; import javax.swing.*; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.image.B…

vue2 echarts饼图,双柱图

<template><div><div class"toQ"><el-row><el-col :span"12"><div class"toW"><el-card><div class"data-title"><div class"toE">周杰伦</div></div>&…

Vscode新手安装与使用

安装与版本选择 VS Code 有两个不同的发布渠道&#xff1a;一个是我们经常使用的稳定版&#xff08;Stable&#xff09;&#xff0c;每个月发布一个主版本&#xff1b;另外一个发布渠道叫做 Insiders&#xff0c;每周一到周五 UTC 时间早上6点从最新的代码发布一个版本&#x…

java设计模式学习之【模板方法模式】

文章目录 引言模板方法模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用游戏设计示例代码地址 引言 设想你正在准备一顿晚餐&#xff0c;无论你想做意大利面、披萨还是沙拉&#xff0c;制作过程中都有一些共同的步骤&#xff1a;准备原料、加工食物、摆盘。…

一文搞懂Go GC演进史,讲的太细致了!

最近在和 Go就业训练营 的朋友讨论Go GC的问题&#xff0c;发现了刘丹冰老师总结的内容&#xff0c;写的太好了&#xff0c;和大家分享一下。 我们的讨论和思考也整理到这篇文章中了&#xff0c;希望对你有启发。 垃圾回收(Garbage Collection&#xff0c;简称GC)是编程语言中…

【Vue2+3入门到实战】(13)插槽<slot>详细示例及自定义组件的创建与使用代码示例 详解

目录 一、学习目标1.插槽2.综合案例&#xff1a;商品列表 一、插槽-默认插槽1.作用2.需求3.问题4.插槽的基本语法5.代码示例6.总结 二、插槽-后备内容&#xff08;默认值&#xff09;1.问题2.插槽的后备内容3.语法4.效果5.代码示例 三、插槽-具名插槽1.需求2.具名插槽语法3.v-s…

Hadoop安装笔记2单机/伪分布式配置_Hadoop3.1.3——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

紧接着上一篇博客&#xff1a;Hadoop安装笔记1&#xff1a; Hadoop安装笔记1单机/伪分布式配置_Hadoop3.1.3——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2&#xff1a;离线数据处理-CSDN博客https://blog.csdn.net/Zhiyilang/article/details/135…

CUDA驱动深度学习发展 - 技术全解与实战

全面介绍CUDA与pytorch cuda实战 关注TechLead&#xff0c;分享AI全维度知识。作者拥有10年互联网服务架构、AI产品研发经验、团队管理经验&#xff0c;同济本复旦硕&#xff0c;复旦机器人智能实验室成员&#xff0c;阿里云认证的资深架构师&#xff0c;项目管理专业人士&…

MybatisX逆向工程方法

官方文档链接&#xff1a;MybatisX快速开发插件 | MyBatis-Plus (baomidou.com) 使用MybatisX可以快速生成mapper文件&#xff0c;实体类和service及实现 效果 方法&#xff1a;首先下载mybatisX插件 然后创建数据库信息 然后选中表&#xff0c;右键&#xff0c;点击Mybatis…

java Servlet 汽车保养服务平台系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

一、源码特点 java Servlet 汽车保养服务平台系统是一套完善的java web信息管理系统&#xff0c;采用serlvetdaobean mvc模式开发&#xff0c;对理解JSP java编程开发语言有帮助&#xff0c;系统具有完整的源代码和数 据库&#xff0c;系统主要采用B/S模式开发。开发环境为…

应用在网络摄像机领域中的国产音频ADC芯片

IPC&#xff1a;其实叫“网络摄像机”&#xff0c;是IP Camera的简称。它是在前一代模拟摄像机的基础上&#xff0c;集成了编码模块后的摄像机。它和模拟摄像机的区别&#xff0c;就是在新增的“编码模块”上。模拟摄像机&#xff0c;顾名思义&#xff0c;输出的是模拟视频信号…

专做真人转动漫视频AI——DomoAI(附详细教程)

家人们&#xff01;今天给大家推荐一款近期火爆外网的真人视频转动漫的AI工具——DomoAI&#xff0c;只需提供一张图片&#xff0c;或者一段视频&#xff0c;输入提示词&#xff0c;并指定动漫风格&#xff0c;即可将照片或者视频动漫化&#xff0c;而且生成的画面效果极致丝滑…

演员-评论家算法:多智能体强化学习核心框架

演员-评论家算法 演员-评论家算法&#xff1a;策略梯度算法 DQN 算法演员-评论家的协作流程演员&#xff1a;策略梯度算法计算智能体策略预期奖励的梯度公式分解时间流程拆解 通过采样方法近似估计梯度公式拆解时间流程拆解 改进策略设置基线&#xff1a;适用于减小方差、加速…

使用NTC负温度系数热敏电阻控制温度

鱼缸原来的加热棒使用的是NTC负温度系数的热敏电阻测温&#xff0c;负温度系数是指随着温度的升高&#xff0c;电阻是不断按照指数形式减小的&#xff0c;在22度的情况下实测电阻是10K多&#xff0c;可以断定使用了10K&#xff08;25度下是10K&#xff09;的电阻&#xff0c;为…

推荐几款常用的项目管理工具

项目管理软件可以在帮助项目经理和团队记录、跟踪和分析项目进展。它的主要功能有&#xff1a; 1、任务管理&#xff1a;制定项目计划&#xff0c;并将任务分配给项目成员&#xff0c;监控任务的进度和完成情况。 2、沟通与协作&#xff1a;帮助项目团队成员建立一个有效的沟…

postman使用-04响应

文章目录 响应响应界面说明Pretty&#xff1a;格式化显示&#xff0c;以便查看Raw&#xff1a;不进行任何处理&#xff0c;显示响应数据的原始格式Preview&#xff1a;预览响应体&#xff0c;会自动换行&#xff0c;不会格式化&#xff08;有时候是数据&#xff0c;有时候是页面…

哪种猫粮比较好?性价比高的主食冻干品牌排行榜前五

不知道从什么时候开始掀起一股冻干喂养风&#xff0c;各种查资料阅读文献发现冻干喂养是最适合忙碌地打工人的“生骨肉喂养”替代版&#xff0c;是最符合猫咪饮食天性的一种。很多养猫人纷纷开始冻干喂养&#xff0c;但对于主食冻干猫粮的选择就让很多猫奴犯了难在电商平台随便…