返回

找出数组中缺失的第一个正数:LeetCode 41

前端

导言

在计算机科学中,查找数组中缺失的第一个正数是一个经典问题。这个问题有多种解决方法,每种方法都有其独特的优点和缺点。本文将介绍 LeetCode 41 题的两种高效解决方案,并深入探讨每种方法的复杂度和实现细节。

LeetCode 41 题

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

解决方案 1:哈希表法

思想: 哈希表法是一种使用哈希表来存储数组中出现的数字的方法。一旦数组中的所有数字都被添加到哈希表中,我们可以遍历正整数,直到找到一个不在哈希表中的数字。这个数字就是数组中缺失的第一个正数。

算法步骤:

  1. 创建一个哈希表,将数组中所有数字作为键添加到其中。
  2. 从正整数 1 开始遍历,直到找到一个不在哈希表中的数字。
  3. 将找到的数字返回为缺失的第一个正数。

代码实现:

def first_missing_positive_hash(nums):
    hashset = set(nums)
    i = 1
    while i in hashset:
        i += 1
    return i

复杂度分析:

  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。遍历数组需要 O(n) 时间,遍历正整数也需要 O(n) 时间。
  • 空间复杂度:O(n),因为哈希表存储了数组中的所有数字。

解决方案 2:置换法

思想: 置换法是一种利用数组中元素的正负符号来标识缺失数字的方法。我们遍历数组,将正数移动到数组的开头,将负数移动到数组的末尾。然后,我们遍历数组的正数部分,将每个数字交换到其索引 + 1 的位置。最后,我们遍历数组的正数部分,找到第一个不在正确位置的数字。这个数字就是数组中缺失的第一个正数。

算法步骤:

  1. 遍历数组,将正数移动到数组的开头,将负数移动到数组的末尾。
  2. 遍历数组的正数部分,将每个数字交换到其索引 + 1 的位置。
  3. 遍历数组的正数部分,找到第一个不在正确位置的数字。
  4. 将找到的数字返回为缺失的第一个正数。

代码实现:

def first_missing_positive_swap(nums):
    n = len(nums)
    i = 0
    j = n - 1

    # 将正数移动到数组的开头,负数移动到数组的末尾
    while i <= j:
        if nums[i] > 0:
            nums[i], nums[j] = nums[j], nums[i]
            j -= 1
        else:
            i += 1

    # 将每个正数交换到其索引 + 1 的位置
    for i in range(n):
        if nums[i] <= 0 or nums[i] > n:
            nums[i] = n + 1
        else:
            index = nums[i] - 1
            while nums[index] > 0:
                temp = nums[index]
                nums[index] = -1
                index = temp - 1

    # 找到第一个不在正确位置的数字
    for i in range(n):
        if nums[i] > 0:
            return i + 1

    return n + 1

复杂度分析:

  • 时间复杂度:O(n),其中 n 是数组 nums 的长度。遍历数组和交换数字都需要 O(n) 时间。
  • 空间复杂度:O(1),因为我们没有使用额外的空间来存储中间结果。

总结

LeetCode 41 题的两种解决方案——哈希表法和置换法——都有其优点和缺点。哈希表法更简单、更容易理解,但空间复杂度更高。置换法空间复杂度更低,但算法步骤更复杂。根据具体情况,选择最适合的解决方案。

练习

  1. 尝试使用两种方法解决 LeetCode 41 题。
  2. 探索哈希表法和置换法在处理其他类型问题时的应用。
  3. 设计自己的算法来解决 LeetCode 41 题。

补充阅读