缺失的第一个正数(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://www.mzph.cn/news/583656.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

WPF Button使用漂亮 控件模板ControlTemplate 按钮使用控制模板实例及源代码 设计一个具有圆角边框、鼠标悬停时颜色变化的按钮模板

续前两篇模板文章 模板介绍1 模板介绍2 WPF中的Button控件默认样式简洁&#xff0c;但可以通过设置模板来实现更丰富的视觉效果和交互体验。按钮模板主要包括背景、边框、内容&#xff08;通常为文本或图像&#xff09;等元素。通过自定义模板&#xff0c;我们可以改…

会议室占用时间段 - 华为OD统一考试

OD统一考试 题解: Java / Python / C++ 题目描述 现有若干个会议,所有会议共享一个会议室,用数组表示各个会议的开始时间和结束时间, 格式为: [[会议1开始时间,会议1结束时间],[会议2开始时间,会议2结束时间]] 请计算会议室占用时间段。 输入描述 [[会议1开始时间,…

this和static和const关键字

1. this指针 1.1 基本认识 C中成员函数的特性&#xff0c;在类的非静态成员函数中都会存在一个this指针&#xff0c;来指向当前类&#xff0c;它是隐式的不可见的&#xff0c;是作为函数的第一个参数可以通过this指针来解决二义性的问题 是的&#xff0c;this指针是与类的实例相…

最快速度与最简代码搭建卷积神经网络,并快速训练模型,每日坚持手撕默写代码

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下最快速度与最简代码搭建卷积神经网络&#xff0c;并快速训练模型&#xff0c;每日坚持手撕默写代码。随着人工智能的快速发展&#xff0c;去年有强大的大模型ChatGPT横空出世&#xff0c;国内的大模型也紧追其后的发…

写一个java状态模式的详细实例

以下是一个示例的 Java 状态模式实现&#xff1a; java Copy code // 定义状态接口 interface State { void handleState(Context context); } // 具体状态类 1 class ConcreteState1 implements State { public void handleState(Context context) { System…

基于ssm西安旅游管理系统论文

摘 要 在如今社会上&#xff0c;关于信息上面的处理&#xff0c;没有任何一个企业或者个人会忽视&#xff0c;如何让信息急速传递&#xff0c;并且归档储存查询&#xff0c;采用之前的纸张记录模式已经不符合当前使用要求了。所以&#xff0c;对西安旅游信息管理的提升&#x…

JavaScript----字符串拼接

1、字符串拼接 字符串拼接使用: "" 运算符 var iNum1 10; var fNum2 11.1; var sStr abc;result iNum1 fNum2; alert(result); // 弹出21.1result fNum2 sStr; alert(result); // 弹出11.1abc说明 数字和字符串拼接会自动进行类型转换(隐士类型转换)&#…

使用pandas绘图,并保存,支持中文

使用pandas绘图&#xff0c;并保存&#xff0c;支持中文 支持中文标题绘图创建DataFrame绘制图形添加其他绘图细节保存图形显示图形 支持中文标题 import matplotlib.pyplot as plt from matplotlib.font_manager import FontProperties import matplotlib.font_manager as fm…

深入理解Java集合框架

导语&#xff1a; Java集合框架是Java提供的一组用于管理对象的类和接口&#xff0c;它是Java编程中非常重要的一部分。Java集合框架通过提供诸如List、Set、Map等数据结构&#xff0c;为程序员提供了一种方便、高效的管理对象的方式。本文将深入理解Java集合框架&#xff0c;包…

RuntimeError: “slow_conv2d_cpu“ not implemented for ‘Half‘

目录 临时解决方法&#xff1a; RuntimeError: "slow_conv2d_cpu" not implemented for Half train_lora.py中&#xff1a; 原因&#xff1a;cpu不支持fp16类型&#xff0c; 临时解决方法&#xff1a; 注释掉fp16模式&#xff0c; weight_dtype torch.float32i…

Mediapipe绘制实时3d铰接骨架图——Mediapipe实时姿态估计

一、前言 大约两年前&#xff0c;基于自己的理解我曾写了几篇关于Mediapipe的文章&#xff0c;似乎帮助到了一些人。这两年&#xff0c;忙于比赛、实习、毕业、工作和考研。上篇文章已经是一年多前发的了。这段时间收到很多私信和评论&#xff0c;请原谅无法一一回复了。我将尝…

Redis缓存与数据库如何保证一致性

数据库和缓存如何保证一致性&#xff1f; 目录 数据库和缓存如何保证一致性&#xff1f;背景方案先更新数据库&#xff0c;还是先更新缓存&#xff1f;先更新数据库&#xff0c;再更新缓存先更新缓存&#xff0c;再更新数据库 先更新数据库&#xff0c;还是先删除缓存&#xff…

安装 PyQt5 保姆级教程

作者&#xff1a;billy 版权声明&#xff1a;著作权归作者所有&#xff0c;商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处 前言 博主之前做应用层开发用的一直是 Qt&#xff0c;这次尝试一下在 python 中使用 Pyqt5 模块来开发 UI 界面&#xff0c;这里做一些…

certum的ip证书购买流程

Certum是成立于欧洲的CA认证机构&#xff0c;经过二十几年的发展Certum已经成为欧洲知名的CA认证机构之一&#xff0c;拥有广泛的客户群体和合作伙伴。IP证书是Certum为只有公网IP地址的网站准备的数字加密服务。今天就随SSL盾小编了解购买Certum旗下的IP证书流程。 第一步&am…

WPF Grid

Resource 在 WPF 中&#xff0c;“Grid” 是一种用于布局的面板控件&#xff0c;而 “Resource” 是一种用于定义可重用对象的机制。您可以将资源定义为 Grid 控件的一部分&#xff0c;以便在整个应用程序中共享和重用。 使用资源可以帮助您简化界面的创建和维护。在 Grid 控件…

总结一些好用的函数

1. <string.h>/<cstring>头文件 中的 memset函数 作用&#xff1a;用于将一段内存区域设置为特定的值(它作用的基本单位是字节) 可以对变量&#xff0c;数组&#xff08;一维数组和二维数组&#xff09;&#xff0c;结构体进行初始化&#xff0c;但是不能对vecto…

数据库进阶教学——读写分离(Mycat1.6+Ubuntu22.04主+Win10从)

目录 1、概述 2、环境准备 3、读写分离实验 3.1、安装jdk 3.2、安装Mycat 3.3、配置Mycat 3.3.1、配置schema.xml ​​​​3.3.2、配置server.xml 3.4、修改主从机远程登陆权限 3.4.1、主机 3.4.2、从机 3.5、启动Mycat 3.6、登录Mycat 3.7、验证 1、概述 读写分…

如何合理配置云服务器的CPU和内存?

​  提到云服务器性能&#xff0c;大抵有两个主要影响因素&#xff0c;CPU 核心数量和内存容量 &#xff0c;它们决定了云服务器的速度和可靠性。日常运用中&#xff0c;我们如何判断网站需要需要更多或更少?如何扩大或缩小它们以优化网站的性能? 一般来说&#xff0c;您拥…

视频遥测终端机的设计需求

目录 1.目的 2.参考文件 3.总体描述 4.硬件资源描述 4.1微控制单元 4.2视频处理单元 4.3性能指标 5.功能要求 5.1系统参数要求 5.1.1系统管理 5.1.2系统配置 5.1.2.1一般参数 5.1.2.2编码参数 5.1.2.3网络参数 5.1.2.4网络服务 5.1.2.5OSD参数 5.1.2.6抓拍 5.…

java设计模式学习之【访问者模式】

文章目录 引言访问者模式简介定义与用途实现方式 使用场景优势与劣势在Spring框架中的应用电脑示例代码地址 引言 设想你是一个艺术馆的管理员&#xff0c;艺术馆里有各种各样的艺术品。每当有游客来访时&#xff0c;根据他们的兴趣&#xff0c;他们可能只想看画、雕塑或特定的…