力扣最热一百题——缺失的第一个正数
目录
题目链接:41. 缺失的第一个正数 - 力扣(LeetCode)
题目描述
示例
提示:
解法一:标记数组法
1. 将非正数和超出范围的数替换
2. 使用数组下标标记存在的数字
3. 找到第一个未标记的位置
4. 为什么时间复杂度是 O(n)?
5. 常数空间?
Java写法:
运行时间
C++写法:
运行时间
时间复杂度以及空间复杂度
解法二:交换至正确的位置
1. 将每个数放到正确的位置上
2. 查找第一个未按顺序排列的位置
3. 如果所有数字都按顺序排列
为什么时间复杂度是 O(n)?
为什么空间复杂度是 O(1)?
困惑为什么交换的时候是while而不是if
Java解法:
运行时间
C++解法:
运行时间
时间复杂度以及空间复杂度
总结
题目链接:41. 缺失的第一个正数 - 力扣(LeetCode)
注:下述题目描述和示例均来自力扣
题目描述
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
示例
示例 1:
输入:nums = [1,2,0] 输出:3 解释:范围 [1,2] 中的数字都在数组中。
示例 2:
输入:nums = [3,4,-1,1] 输出:2 解释:1 在数组中,但 2 没有。
示例 3:
输入:nums = [7,8,9,11,12] 输出:1 解释:最小的正数 1 没有出现。
提示:
1 <= nums.length <= 10^5
-2^31 <= nums[i] <= 2^31 - 1
解法一:标记数组法
1. 将非正数和超出范围的数替换
for (int i = 0; i < n; ++i) {if (nums[i] <= 0) {nums[i] = n + 1;}
}
首先,代码遍历数组 nums
,将其中所有非正数(即小于等于0的数)或大于 n
的数(n
是数组的长度)替换为 n + 1
。因为我们只关心数组中出现的正整数,且最小的正整数应该在1到 n
的范围内,所以将这些不相关的数(非正数和大于 n
的数)统一设置为 n + 1
(一个无效的值,确保不会干扰后续的逻辑)。
2. 使用数组下标标记存在的数字
for (int i = 0; i < n; ++i) {int num = Math.abs(nums[i]);if (num <= n) {nums[num - 1] = -Math.abs(nums[num - 1]);}
}
这一步的目标是通过数组中的数字来标记哪些正整数是存在的。具体逻辑是:
- 对每个数
num = abs(nums[i])
,如果num
在1到n
的范围内,则将nums[num-1]
的值设为负数。这相当于利用下标num-1
来记录数字num
是否出现过。 - 如果某个数字
num
出现了,就将位置num-1
上的数字设为负数,表示该位置已经被标记。
3. 找到第一个未标记的位置
for (int i = 0; i < n; ++i) {if (nums[i] > 0) {return i + 1;}
}
return n + 1;
在这一步,代码再次遍历数组,查找第一个值为正数的下标 i
,表示 i+1
这个数字没有出现过。因为在第二步中,所有出现过的数字的对应位置已经被标记为负数,所以第一个正数的位置就是缺失的最小正整数。
4. 为什么时间复杂度是 O(n)?
- 每个元素最多被处理两次:第一次是在将非正数替换为
n+1
时,第二次是在通过下标标记数字时。因此,总体的遍历次数是线性的,即 O(n)。
5. 常数空间?
- 除了输入数组
nums
本身外,代码没有使用额外的数据结构(比如数组、栈、队列等)。空间复杂度是 O(1),因为对数组的修改都是就地进行的。
Java写法:
class Solution {public int firstMissingPositive(int[] nums) {// 取得数组的长度int n = nums.length;// 由于负数并不在我们的考虑范围里面,所以全部放到涉及不到的地方N+1for (int i = 0; i < n; i++) {if (nums[i] <= 0) {nums[i] = n + 1;}}// 将每个数都打上“标记”for (int i = 0; i < n; i++) {// 由于这里的数可能在打标记的过程中被修改为负数// 所以在这里再取值的时候要取为绝对值int num = Math.abs(nums[i]);if (num <= n) {// 采用绝对值的负数,防止被打两次负数变成正数nums[num - 1] = -Math.abs(nums[num - 1]);}}// 找有没有是正数的,有的话就是他的位置for (int i = 0; i < n; i++) {if (nums[i] > 0) {return i + 1;}}// 刚才没有找到正数,那么就是数组长度加一的位置了return n + 1;}
}
运行时间
C++写法:
class Solution {
public:int firstMissingPositive(vector<int>& nums) {// 由于在java那里的注释我已经写的很详细了,这里我就随便写写了// 获取数组的长度int n = nums.size();// 将小于等于零的数(非正整数)都设置为无关紧要的位置也就是n+1for(int i = 0; i < n; i++){if(nums[i] <= 0){nums[i] = n + 1;}}// 打标记,将在[1,n+1]中的数,其大小对应的下标-1上的数置为负数for(int i = 0; i < n; i++){int num = abs(nums[i]);if(num <= n){// 为了防止nums[num - 1]已经被标记(取为负数)这里取绝对值nums[num - 1] = -abs(nums[num - 1]);}}// 找有没有正数for(int i = 0; i < n; i++){if(nums[i] > 0){// 找到了return i + 1;}}// 没找到return n + 1;}
};
运行时间
时间复杂度以及空间复杂度
| |
解法二:交换至正确的位置
1. 将每个数放到正确的位置上
for (int i = 0; i < n; ++i) {while (nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] != nums[i]) {int temp = nums[nums[i] - 1];nums[nums[i] - 1] = nums[i];nums[i] = temp;}
}
这一部分的核心思想是将数组中的数字放到正确的位置上。每个数字 nums[i]
,如果它在1到 n
的范围内,那么它应该出现在数组的第 nums[i] - 1
个位置。
- 通过
while
循环,代码不断检查nums[i]
是否满足以下条件:nums[i]
是正数,并且在1到n
的范围内。nums[i]
还没有被放到它应该在的位置上(即nums[nums[i] - 1] != nums[i]
)。
- 如果条件满足,就将
nums[i]
与它应该在的位置nums[nums[i] - 1]
进行交换,直到每个数都被放到了正确的位置上,或者nums[i]
已经不需要再交换了。
这个过程类似于 "桶排序"(Cyclic Sort)的思想,把数组看作一个映射,通过交换将每个数字放到对应的桶(即数组位置)中。
2. 查找第一个未按顺序排列的位置
for (int i = 0; i < n; ++i) {if (nums[i] != i + 1) {return i + 1;}
}
在第二部分,代码再次遍历数组,寻找第一个下标 i
,使得 nums[i] != i + 1
,即第 i+1
这个数字没有出现在数组中。如果找到这样的位置,就说明 i + 1
是第一个缺失的最小正整数。
3. 如果所有数字都按顺序排列
return n + 1;
如果所有数字都已经按顺序排列了,那么数组中的数从 1
到 n
都出现过,这时缺失的最小正整数是 n + 1
。
为什么时间复杂度是 O(n)?
- 每个元素最多会被交换一次,因为每次交换都把元素放到其正确的位置上。交换的次数是有限的,因此整个过程的时间复杂度是 O(n)。
为什么空间复杂度是 O(1)?
- 除了输入数组本身外,代码没有使用额外的数据结构,交换操作是就地进行的,因此空间复杂度为 O(1)。
困惑为什么交换的时候是while而不是if
-
多个元素错位的情况:
在这个问题中,目标是将每个元素放到它的正确位置,例如数字k
应该放在数组的第k-1
位置上。由于数组是未排序的,一个元素在经过一次交换后,它可能仍然没有被放到正确的位置。因此,代码需要反复检查并继续交换,直到这个元素被放置在正确的位置上。 -
确保每个元素到达正确的位置:
使用while
的目的是让每个元素继续交换,直到它符合条件为止,即nums[i] > 0 && nums[i] <= n && nums[nums[i] - 1] == nums[i]
。如果使用if
,只能在当前条件下交换一次,但在某些情况下,交换一次后新的nums[i]
可能仍然需要继续交换。例如,如果两个错位的元素在第一次交换后,新的元素也不在正确位置上,则需要再次交换。 -
示例: 假设数组是
[3, 4, -1, 1]
,我们希望让每个元素放在正确的位置:- 第一个元素
3
应该放在位置2
(即下标3 - 1 = 2
)。 - 交换之后数组变为:
[-1, 4, 3, 1]
。注意此时nums[0]
变为了-1
。 - 但是第一个元素现在是
-1
,不符合条件(nums[0] > 0 && nums[0] <= n
),所以停止处理这个位置。 - 接着处理第二个元素
4
。4
应该在位置3
,交换后数组变为:[-1, 1, 3, 4]
。此时nums[1] = 1
也不在正确的位置,需要再一次交换,把1
放到位置0
。 - 通过
while
循环,1
被正确放置在位置0
,最终得到数组[1, -1, 3, 4]
。
如果这里使用的是
if
而不是while
,那么在某些情况下,数组中的元素可能没有被交换到最终的正确位置,需要额外的遍历或逻辑来完成任务。 - 第一个元素
Java解法:
class Solution {public int firstMissingPositive(int[] nums) {// 先获取数组的长度int len = nums.length;// 进入交换数组的逻辑for(int i = 0; i < len; i++){// 由于nums[i](本来的值)和nums[nums[i] - 1](应该在的位置的值)// 可能在交换之后不在正确的位置上,所以需要一直交换,直到在正确的位置上while(nums[i] >= 1 && nums[i] <= len && nums[i] != nums[nums[i] - 1]){int temp = nums[i];nums[i] = nums[nums[i] - 1];nums[temp - 1] = temp;}}// 找自己数值跟位置对不上的for(int i = 0; i < len; i++){if(nums[i] != i + 1){return i + 1;}}// 那就是最后一位了return len + 1;}
}
运行时间
C++解法:
class Solution {
public:int firstMissingPositive(vector<int>& nums) {// 由于在java那里的注释我已经写的很详细了,这里我就随便写写了// 获取数组的长度int len = nums.size();// 交换逻辑,确保数字放到正确的位置for (int i = 0; i < len; i++) {// 不断交换,直到当前的nums[i]是有效值并且放在正确的位置上while (nums[i] >= 1 && nums[i] <= len && nums[i] != nums[nums[i] - 1]) {// 交换时也需要防止nums[i]的值被覆盖后产生的错误int temp = nums[i];nums[i] = nums[temp - 1];nums[temp - 1] = temp;}}// 检查第一个位置不正确的元素for (int i = 0; i < len; i++) {if (nums[i] != i + 1) {return i + 1;}}// 如果所有数字都按顺序排列,则缺少的是len + 1return len + 1;}
};
运行时间
时间复杂度以及空间复杂度
| |
总结
哇塞,不愧是力扣的困难题目,我现在真的越来越喜欢写困难的题目了,每次写完虽然可能要花点时间,但是每次写完都是一次来自大脑的洗涤,思维的活跃,这种逆天的感觉真的很爽,加油吧兄弟们,这几天我凉了,从一天增加100粉丝到只有20几个了,┭┮﹏┭┮
相关文章:

力扣最热一百题——缺失的第一个正数
目录 题目链接:41. 缺失的第一个正数 - 力扣(LeetCode) 题目描述 示例 提示: 解法一:标记数组法 1. 将非正数和超出范围的数替换 2. 使用数组下标标记存在的数字 3. 找到第一个未标记的位置 4. 为什么时间复杂…...

零基础入门AI:一键本地运行各种开源大语言模型 - Ollama
什么是 Ollama? Ollama 是一个可以在本地部署和管理开源大语言模型的框架,由于它极大的简化了开源大语言模型的安装和配置细节,一经推出就广受好评,目前已在github上获得了46k star。 不管是著名的羊驼系列,还是最新…...

3.接口测试的基础/接口关联(Jmeter工具/场景一:我一个人负责所有的接口,项目规模不大)
一、Jmeter接口测试实战 1.场景一:我一个人负责所有的接口:项目规模不大 http:80 https:443 接口文档一般是开发给的,如果没有那就需要抓包。 请求默认值: 2.请求: 请求方式:get,post 请求路径 请求参数 查询字符串参数…...

【matlab】将程序打包为exe文件(matlab r2023a为例)
文章目录 一、安装运行时环境1.1 安装1.2 简介 二、打包三、打包文件为什么很大 一、安装运行时环境 使用 Application Compiler 来将程序打包为exe,相当于你使用C编译器把C语言编译成可执行程序。 在matlab菜单栏–App下面可以看到Application Compiler。 或者在…...
从底层原理上解释clickhouse查询为什么快
ClickHouse 是一个开源的列式数据库管理系统,以其极高的查询性能著称。为了理解 ClickHouse 查询为什么快,我们需要从以下几个方面进行深入探讨,包括其架构设计、存储引擎、索引结构、并行化策略以及内存管理等底层原理。 1. 列式存储&#…...

FEAD:fNIRS-EEG情感数据库(视频刺激)
摘要 本文提出了一种可用于训练情绪识别模型的fNIRS-EEG情感数据库——FEAD。研究共记录了37名被试的脑电活动和脑血流动力学反应,以及被试对24种情绪视听刺激的分类和维度评分。探讨了神经生理信号与主观评分之间的关系,并在前额叶皮层区域发现了显著的…...

标准库标头 <bit>(C++20)学习
<bit>头文件是数值库的一部分。定义用于访问、操作和处理各个位和位序列的函数。例如,有函数可以旋转位、查找连续集或已清除位的数量、查看某个数是否为 2 的整数幂、查找表示数字的最小位数等。 类型 endian (C20) 指示标量类型的端序 (枚举) 函数 bit_ca…...

redis群集三种模式:主从复制、哨兵、集群
redis群集有三种模式 redis群集有三种模式,分别是主从同步/复制、哨兵模式、Cluster,下面会讲解一下三种模式的工作方式,以及如何搭建cluster群集 ●主从复制:主从复制是高可用Redis的基础,哨兵和集群都是在主从复制…...

【MATLAB源码-第225期】基于matlab的计算器GUI设计仿真,能够实现基础运算,三角函数以及幂运算
操作环境: MATLAB 2022a 1、算法描述 界面布局 计算器界面的主要元素分为几大部分:显示屏、功能按钮、数字按钮和操作符按钮。 显示屏 显示屏(Edit Text):位于界面顶部中央,用于显示用户输入的表达式和…...

基于yolov8的红外小目标无人机飞鸟检测系统python源码+onnx模型+评估指标曲线+精美GUI界面
【算法介绍】 基于YOLOv8的红外小目标无人机与飞鸟检测系统是一项集成了前沿技术的创新解决方案。该系统利用YOLOv8深度学习模型的强大目标检测能力,结合红外成像技术,实现了对小型无人机和飞鸟等低空飞行目标的快速、准确检测。 YOLOv8作为YOLO系列的…...

网络封装分用
目录 1,交换机 2,IP 3,接口号 4,协议 分层协议的好处: 5,OSI七层网络模型. 6,TCP/IP五层网络模型(主流): [站在发送方视角] [接收方视角] 1,交换机 交换机和IP没有关系,相当于是对路由器接口的扩充,这时相当于主机都与路由器相连处于局域网中,把越来越多的路由器连接起…...

【Finetune】(一)、transformers之BitFit微调
文章目录 0、参数微调简介1、常见的微调方法2、代码实战2.1、导包2.2、加载数据集2.3、数据集处理2.4、创建模型2.5、BitFit微调*2.6、配置模型参数2.7、创建训练器2.8、模型训练2.9、模型推理 0、参数微调简介 参数微调方法是仅对模型的一小部分的参数(这一小部分可…...
ubuntu24系统普通用户免密切换到root用户
普通用户登录系统后需要切换到root用户,这边需要密码,现在不想让用户知道密码是多少。 sudo: 1 incorrect password attempt $ su - Password: root-security-cm5:~#开始配置普通用户免密切换到root用户,编辑配置文件 /etc/sudoers 最后增加…...
如何应对pcdn技术中遇到的网络安全问题?
在应对网络安全问题时,需要采取一系列的操作措施,以确保网络环境的稳定性和数据的安全性。以下是一些建议: 选择可靠的PCDN提供商:与有良好安全记录的PCDN提供商合作,确保提供商具备专业的安全团队,能够提…...

【WRF工具】WRF Domain Wizard第一期:软件下载及安装
【WRF工具介绍】WRF Domain Wizard下载及安装 1 WRF Domain Wizard 的主要功能2 使用 WRF Domain Wizard 的步骤2.1 安装 WRF Domain Wizard:2.2 启动 WRF Domain Wizard:2.3 定义计算域:2.4 生成配置文件:2.5 运行 WPS 和 WRF&am…...

使用CUBE_MX实现STM32 DMA功能 (储存器发送数据到外设串口)+(外设串口将数据写入到存储器)
目录 一、配置串口打印(参考串口打印的文章) 二、CUBE_MX配置 三、KEIL5配置 1.打开dma.c文件(默认初始化DMA中断函数) 2.打开usart.c文件 3.打开main.c文件(储存器发送数据到外设串口) 4.打开main.c…...

【JavaScript】数据结构之树
什么是树形结构? 一种分层数据的抽象模型,用来分层级关系的。虚拟dom它所组织的那个数据原理就是树形结构 深度优先搜索(遍历)- 递归 从根出发,尽可能深的搜索树的节点技巧 访问根节点对根节点的children挨个进行深…...

【AI大模型】LLM主流开源大模型介绍
目录 🍔 LLM主流大模型类别 🍔 ChatGLM-6B模型 2.1 训练目标 2.2 模型结构 2.3 模型配置(6B) 2.4 硬件要求 2.5 模型特点 2.6 衍生应用 🍔 LLaMA模型 3.1 训练目标 3.2 模型结构 3.3 模型配置(7B) 3.4 硬件…...

Uniapp的alertDialog返回值+async/await处理确定/取消问题
今天在使用uniui的alertDialog时,想添加一个确定/取消的警告框时 发现alertDialog和下面的处理同步进行了,没有等待alaertDialog处理完才进行 查询后发现问题在于 await 关键字虽然被用来等待 alertDialog.value.open() 的完成,但是 alertDi…...
Spring Boot中的响应与分层解耦架构
Spring Boot中的响应与分层解耦架构 在Spring Boot框架中,响应与分层解耦架构是两个核心概念,它们共同促进了应用程序的高效性、可维护性和可扩展性。下面将详细探讨这两个方面,包括Spring Boot的响应机制、分层解耦的三层架构以及它们在实际…...
应用升级/灾备测试时使用guarantee 闪回点迅速回退
1.场景 应用要升级,当升级失败时,数据库回退到升级前. 要测试系统,测试完成后,数据库要回退到测试前。 相对于RMAN恢复需要很长时间, 数据库闪回只需要几分钟。 2.技术实现 数据库设置 2个db_recovery参数 创建guarantee闪回点,不需要开启数据库闪回。…...

AI Agent与Agentic AI:原理、应用、挑战与未来展望
文章目录 一、引言二、AI Agent与Agentic AI的兴起2.1 技术契机与生态成熟2.2 Agent的定义与特征2.3 Agent的发展历程 三、AI Agent的核心技术栈解密3.1 感知模块代码示例:使用Python和OpenCV进行图像识别 3.2 认知与决策模块代码示例:使用OpenAI GPT-3进…...

visual studio 2022更改主题为深色
visual studio 2022更改主题为深色 点击visual studio 上方的 工具-> 选项 在选项窗口中,选择 环境 -> 常规 ,将其中的颜色主题改成深色 点击确定,更改完成...

CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
条件运算符
C中的三目运算符(也称条件运算符,英文:ternary operator)是一种简洁的条件选择语句,语法如下: 条件表达式 ? 表达式1 : 表达式2• 如果“条件表达式”为true,则整个表达式的结果为“表达式1”…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
OkHttp 中实现断点续传 demo
在 OkHttp 中实现断点续传主要通过以下步骤完成,核心是利用 HTTP 协议的 Range 请求头指定下载范围: 实现原理 Range 请求头:向服务器请求文件的特定字节范围(如 Range: bytes1024-) 本地文件记录:保存已…...

CocosCreator 之 JavaScript/TypeScript和Java的相互交互
引擎版本: 3.8.1 语言: JavaScript/TypeScript、C、Java 环境:Window 参考:Java原生反射机制 您好,我是鹤九日! 回顾 在上篇文章中:CocosCreator Android项目接入UnityAds 广告SDK。 我们简单讲…...
WEB3全栈开发——面试专业技能点P2智能合约开发(Solidity)
一、Solidity合约开发 下面是 Solidity 合约开发 的概念、代码示例及讲解,适合用作学习或写简历项目背景说明。 🧠 一、概念简介:Solidity 合约开发 Solidity 是一种专门为 以太坊(Ethereum)平台编写智能合约的高级编…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...