一、题目
给你一个未排序的整数数组 nums
,请你找出其中没有出现的最小的正整数。
请你实现时间复杂度为 O(n)
并且只使用常数级别额外空间的解决方案。
二、解题思路
-
遍历数组:首先,我们需要遍历数组,找到所有负数和零,并将它们替换为一个特定的值(比如数组的最大值加一),这样我们就可以将问题简化为只考虑正整数。
-
标记数字:接下来,我们将数组中的数字与从1开始的正整数进行比较。如果数组中的数字大于0且小于或等于数组的长度,我们将其视为一个标记,将数组中对应位置的值变为负数。这样做的目的是标记出所有已经出现的正整数。
-
找出未标记的数字:经过上述步骤,数组中值为正的数字对应的是未出现的正整数。我们再次遍历数组,找到第一个正数的位置,这个位置的索引加1就是缺失的最小正整数。
三、具体代码
class Solution {
public int firstMissingPositive(int[] nums) {
int n = nums.length;
// Step 1: Replace all negative numbers and zeros with a large number
for (int i = 0; i < n; i++) {
if (nums[i] <= 0) {
nums[i] = n + 1;
}
}
// Step 2: Mark the numbers as present by flipping the sign
for (int i = 0; i < n; i++) {
int num = Math.abs(nums[i]);
if (num <= n) {
nums[num - 1] = -Math.abs(nums[num - 1]);
}
}
// Step 3: Find the first positive index
for (int i = 0; i < n; i++) {
if (nums[i] > 0) {
return i + 1;
}
}
// If all numbers are present, the answer is n + 1
return n + 1;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 代码中包含了三个循环,每个循环都遍历了数组中的每个元素一次。
- 因此,总的操作次数是三次数组长度,即 3n,这使得时间复杂度为 O(n)。
2. 空间复杂度
- 代码中没有使用任何额外的数据结构或变量来存储除了输入数组之外的信息。
- 所有的操作都是在原数组上进行的,因此空间复杂度是常数级别,即 O(1)。
五、总结知识点
-
数组操作:代码主要处理数组,包括数组的遍历、赋值和条件判断。这是解决此类问题的基础。
-
原地修改:算法在不使用额外空间的情况下对输入数组进行原地修改。这意味着所有的操作都是在输入数组上直接进行的,而不是创建新的数组或使用其他数据结构。
-
条件判断:代码中使用了条件判断来检查数组中的元素是否为非正数,并将其替换为一个大于数组长度的数,这样做是为了在后续步骤中能够区分正整数和非正整数。
-
数学函数:
Math.abs()
函数被用来获取数值的绝对值,这是为了确保在标记数组时不区分正负号。 -
位掩码:通过将数组中的正整数取反(即将正数变为负数),算法实际上使用了位掩码的技术来标记数组中已经出现过的正整数。这是一种常见的位操作技巧。
-
边界检查:在替换非正数为一个特定值时,代码检查了这个值是否小于或等于数组的长度,这是为了避免越界错误。
-
循环控制:代码中使用了嵌套循环和简单的条件语句来控制循环的执行流程,这是编程中常见的控制结构。
-
返回值:最后,根据数组的最终状态,算法返回缺失的最小正整数。如果没有找到任何正整数,则返回数组长度加一,这表示从1到n的所有正整数都出现了。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。
标签:代码,遍历,正整数,复杂度,缺失,数组,正数,LeetCode From: https://blog.csdn.net/weixin_62860386/article/details/136694806