以下是解法,都已提交通过。
解法一,基于哈希表
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
}
时间复杂度,空间复杂度。
解法二,基于计数排序
不需要记录每个出现的正整数具体出现了几次。这种解法和基于哈希表的解法本质上是一样。
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 // 兜底情况
}
时间复杂度,空间复杂度。
解法三,原地交换
该解法修改了输入的数组,不需要使用额外存储空间。
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
}
每个数组元素至多移动一次就到了它的最终位置,所以时间复杂度。
基于原地交换,空间复杂度。