leetcode
leetcode 1 ~ 50
缺失的第一个正数

缺失的第一个正数

难度:

标签:

题目描述

给你一个未排序的整数数组 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 <= 105
  • -231 <= nums[i] <= 231 - 1

代码结果

运行时间: 32 ms, 内存: 14.8 MB


/*
 * Problem: Find the smallest missing positive integer in an unsorted array.
 * Solution Idea using Java Streams:
 * 1. We filter out the positive numbers and collect them into a set to ensure uniqueness.
 * 2. We iterate from 1 upwards and check the set to find the smallest missing number.
 * 3. Return the first missing positive integer.
 */
 
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
 
public class Solution {
    public int firstMissingPositive(int[] nums) {
        Set<Integer> positiveNumbers = IntStream.of(nums)
                                                .filter(num -> num > 0)
                                                .boxed()
                                                .collect(Collectors.toSet());
 
        int smallestMissing = 1;
        while (positiveNumbers.contains(smallestMissing)) {
            smallestMissing++;
        }
 
        return smallestMissing;
    }
}

解释

方法:

本题解采用原地哈希的思想。目标是将所有在1到n范围内的数放到它们应该在的位置上(即值为i的数放在下标为i-1的位置上)。这样,第一个不满足nums[i] == i + 1的下标i就是缺失的最小正数。首先遍历数组,对于每个元素nums[i],如果它在1到n的范围内且不在正确的位置上,就将它与nums[nums[i] - 1]交换,直到无法交换为止。然后再次遍历数组,找到第一个不满足nums[i] == i + 1的下标i,返回i + 1作为缺失的最小正数。如果所有数都在正确的位置上,则返回n + 1。

时间复杂度:

O(n)

空间复杂度:

O(1)

代码细节讲解

🦆
为什么在判断nums[i]是否需要交换时,要求nums[i]不等于nums[nums[i]-1]?这个条件是如何确保交换的正确性和避免无限循环的?
这个条件确保了只有当nums[i]不在其正确位置时才进行交换。正确的位置是指值x应该位于索引x-1的位置上。如果没有这个条件,即nums[i]已经等于nums[nums[i]-1],则表示nums[i]已经在正确的位置上,无需再交换。如果忽略这个条件,交换操作可能会导致不必要的重复交换或更糟糕的是,当存在重复元素时可能导致无限循环。例如,如果两个相同的有效元素在错误的位置,没有这个条件,它们会不断交换,永远不会停止。
🦆
如果数组中包含重复的数字,这种解法处理的如何?是否会影响算法的正确性或效率?
在处理包含重复数字的数组时,上述算法仍然有效,因为算法主要关注将每个元素放在其正确的位置(即值i应该在位置i-1)。即使存在重复,重复的元素会尝试放入其应有的位置,但由于条件检查(nums[i] != nums[nums[i]-1]),不会导致错误的元素放置。然而,重复元素可能会导致一些不必要的交换操作,在某些情况下可能会略微降低效率,但不会影响算法的最终正确性。
🦆
题解中提到,如果所有元素都在正确的位置,则返回n + 1。能否详细解释为什么返回n + 1是正确的?
如果数组中的每个数字都恰好处在其正确的位置上(即值i位于索引i-1),这意味着数组包含了从1到n的所有正整数。在这种情况下,数组中不存在间隙,缺失的最小正数就是紧接在这个连续序列之后的下一个整数,即n + 1。这种情况通常发生在数组正好是从1到n的一个排列的时候。
🦆
本题解采用的原地哈希方法在什么情况下可能会失效或不适用?
原地哈希方法依赖于数组中的每个位置能够存放一个正确的元素。该方法在处理非整数或非单一数据类型的数组时不适用,因为它需要数组元素和数组索引之间存在可预测的映射关系。如果数组过于稀疏(例如,大量的0或负数),虽然方法仍有效,但可能会导致效率低下,因为这些元素不会被放在任何有用的位置。另外,如果数组非常大或存在大量重复数据,虽然理论上仍然适用,但在实际操作中可能会遇到性能瓶颈。

相关问题

丢失的数字

给定一个包含 [0, n] 中 n 个数的数组 nums ,找出 [0, n] 这个范围内没有出现在数组中的那个数。

 

示例 1:

输入:nums = [3,0,1]
输出:2
解释:n = 3,因为有 3 个数字,所以所有的数字都在范围 [0,3] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 2:

输入:nums = [0,1]
输出:2
解释:n = 2,因为有 2 个数字,所以所有的数字都在范围 [0,2] 内。2 是丢失的数字,因为它没有出现在 nums 中。

示例 3:

输入:nums = [9,6,4,2,3,5,7,0,1]
输出:8
解释:n = 9,因为有 9 个数字,所以所有的数字都在范围 [0,9] 内。8 是丢失的数字,因为它没有出现在 nums 中。

示例 4:

输入:nums = [0]
输出:1
解释:n = 1,因为有 1 个数字,所以所有的数字都在范围 [0,1] 内。1 是丢失的数字,因为它没有出现在 nums 中。

 

提示:

  • n == nums.length
  • 1 <= n <= 104
  • 0 <= nums[i] <= n
  • nums 中的所有数字都 独一无二

 

进阶:你能否实现线性时间复杂度、仅使用额外常数空间的算法解决此问题?

寻找重复数

给定一个包含 n + 1 个整数的数组 nums ,其数字都在 [1, n] 范围内(包括 1n),可知至少存在一个重复的整数。

假设 nums 只有 一个重复的整数 ,返回 这个重复的数

你设计的解决方案必须 不修改 数组 nums 且只用常量级 O(1) 的额外空间。

 

示例 1:

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

示例 2:

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

示例 3 :

输入:nums = [3,3,3,3,3]
输出:3

 

 

提示:

  • 1 <= n <= 105
  • nums.length == n + 1
  • 1 <= nums[i] <= n
  • nums只有一个整数 出现 两次或多次 ,其余整数均只出现 一次

 

进阶:

  • 如何证明 nums 中至少存在一个重复的数字?
  • 你可以设计一个线性级时间复杂度 O(n) 的解决方案吗?

找到所有数组中消失的数字

给你一个含 n 个整数的数组 nums ,其中 nums[i] 在区间 [1, n] 内。请你找出所有在 [1, n] 范围内但没有出现在 nums 中的数字,并以数组的形式返回结果。

 

示例 1:

输入:nums = [4,3,2,7,8,2,3,1]
输出:[5,6]

示例 2:

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

 

提示:

  • n == nums.length
  • 1 <= n <= 105
  • 1 <= nums[i] <= n

进阶:你能在不使用额外空间且时间复杂度为 O(n) 的情况下解决这个问题吗? 你可以假定返回的数组不算在额外空间内。

情侣牵手

n 对情侣坐在连续排列的 2n 个座位上,想要牵到对方的手。

人和座位由一个整数数组 row 表示,其中 row[i] 是坐在第 i 个座位上的人的 ID。情侣们按顺序编号,第一对是 (0, 1),第二对是 (2, 3),以此类推,最后一对是 (2n-2, 2n-1)

返回 最少交换座位的次数,以便每对情侣可以并肩坐在一起每次交换可选择任意两人,让他们站起来交换座位。

 

示例 1:

输入: row = [0,2,1,3]
输出: 1
解释: 只需要交换row[1]和row[2]的位置即可。

示例 2:

输入: row = [3,2,0,1]
输出: 0
解释: 无需交换座位,所有的情侣都已经可以手牵手了。

 

提示:

  • 2n == row.length
  • 2 <= n <= 30
  • n 是偶数
  • 0 <= row[i] < 2n
  • row 中所有元素均无重复