leetcode 41. 缺失的第一个正数

226 阅读1分钟

题目链接

以下是解法,都已提交通过。

解法一,基于哈希表

func firstMissingPositive(nums []int) int {
    l := len(nums)
    if l == 0 {
        return 1
    }
    m := make(map[int]struct{}, l)
    for _, n := range nums {
        m[n] = struct{}{}
    }
    // map中记录了已存在的数
    for i := 1; i <= l; i++ { // 遍历正整数区间[1,l]
        if _, ok := m[i]; !ok {
            return i
        }
    }
    return l+1
}

时间复杂度O(n)O(n),空间复杂度O(n)O(n)

解法二,基于计数排序

不需要记录每个出现的正整数具体出现了几次。这种解法和基于哈希表的解法本质上是一样。

func firstMissingPositive(nums []int) int {
    l := len(nums)
    if l == 0 {
        return 1
    }
    ns := make([]int, l+1) // 在索引[1,l]上做统计
    for _, n := range nums {
        if n >= 1 && n <= l { // 如果整数[1,l]某个存在
            ns[n] = 1 // 记录某个正整数存在。只需要记录它出现了即可。
        }
    }
    for i := 1; i <= l; i++ { // 遍历索引[1,l],相当于遍历正整数区间[1,l]
        if ns[i] == 0 { // 哪个是0,它就不存在
            return i
        }
    }
    return l+1 // 兜底情况
}

时间复杂度O(n)O(n),空间复杂度O(n)O(n)

解法三,原地交换

该解法修改了输入的数组,不需要使用额外存储空间。

func firstMissingPositive(nums []int) int {
    l := len(nums)
    if l == 0 {
        return 1
    }
    for i := 0; i < l; {
        ni := nums[i]
        j := ni-1 // ni的目标位置,比如把整数1放在0索引,把整数2放在1索引。
        // ni >= 1 && ni <= l 限制了需要处理的数组,同时使得j一定是一个合法的索引。
        // i != j 说明ni不在它的目标位置。
        // ni != nums[j] 如果和ni相等的值已经在它应该在的索引上了,就不做交换。
        if ni >= 1 && ni <= l && i != j && ni != nums[j] {
            nums[i], nums[j] = nums[j], ni
        } else {
            i++
        }
    }
    for i, n := range nums {
        if i != n-1 {
            return i+1
        }
    }
    return l+1
}

每个数组元素至多移动一次就到了它的最终位置,所以时间复杂度O(n)O(n)
基于原地交换,空间复杂度O(1)O(1)