题目
给你一个未排序的数组,让你找到最小的正整数
如题:
我们当然可以依旧使用暴力算法界问题,但由于题目限定,时间复杂度为O(n),那么暴力算法肯定不能解决,我们可以先简单理一下暴力法的思路,即按照数组的长度,从1到 len(nums) 依次判断,每个数字都要遍历一遍数组确认是否存在,若存在,则返回,若不存在,则直接输出,时间复杂度应该为 O (n2)。
今天我们主要提一下哈希表,用哈希表的算法解一下这道题:
我们先了解一下什么是哈希表:
哈希表
哈希表的基础依旧是数列,但不一样的是有key-value作为指标和索引,和数组相似,在一个数组中,对应的下标可以表示该数组中对应的数列,例如,在[0,2,4,6,8] 中,nums[ 2 ] 就可以代表在这个数组中的 4 这个数字,哈希表也是同理,在某一个关键字 key 中,有一个值 value 与之对应,我们可以通过遍历的方式,利用某个函数模型(哈希函数)将值一一对应到 entry 里面。
那为什么说这个函数优秀呢?
一个很形象的例子,在我们通讯录中,找寻一个人一般不会一个一个找,而是通过首字母来快速定位这个人名字出现的大致范围,而这就是哈希表在算法上的优越性,关于如何使用map,可以点击此处了解,而关于哈希表的通俗讲解,可以在此处看一篇小白相对好理解的博文
解题
然后我们来试图解解一下这个题,同样的问题出现了,不符合这个题目的限定条件,如果我们将每个数组都放入哈希表中,利用key值来快速定位,虽然在时间上可以达到目标,但是在储存上,将和数组的长度一样,为 O( n ) ,所以,我们可以采用官方的解法,将哈希表的方式变动一下,来得出较好的结果。
首先,我们先分析一下这道题,和暴力解法一致的是,缺失的数字一定在 1 到 len(nums) 中产出,若出现负数,则占有了正数的位置,如果是出现了大于长度的数字,则较小的数字就被空下了。
那怎么样用哈希表的思想来解决题目呢,就是像哈希表一样,先行遍历一次数组,然后在下标做一个标记,然后第二次遍历是,只需要找寻这个下标就可以,具体思路如下:
首先解决的是负数的问题,先把所有负数变为1,当然,如果这样的话,就要先查看数组中是否存在 1,若不尊在,则直接返回。
然后,遍历数组,将数组中每一个数字作为下标,找寻到这个位置的数字,将其变为负数,即相当于这个数字被使用过,所以对应的位置打好标记,这样说可能有点难理解,我们用一个例子来解释:
[2, 1 ,4, 6] 在这个数组中,在nums[ 0 ]处的数字为2,则nums[ 2 ] 处的数字变为 -4,再次遍历的时候,这个地方的数字为负数,则下标被放弃,直接跳过,没有被标记的数字,即正数的地方的下标,就是缺失的那个正整数。
具体代码如下:
func firstMissingPositive(nums []int) int {
n := len(nums)
found := 0
//先检查是否有1,若有1,或长度过短,可以直接返回答案
for i := 0; i < l; i++ {
if nums[i] == 1 {
found+=1
break
}
}
if found == 0 {
return 1
}
if n == 1 {
return 2
}
//变为正数做好标记
for i := 0; i < n; i++ {
num := Abs(nums[i])
if num <= n {
nums[num - 1] = -Abs(nums[num - 1])
}
}
//搜索
for i := 0; i < n; i++ {
if nums[i] > 0 {
return i + 1
}
}
return n + 1
}
func Abs(x int) int {
if x < 0 {
return -x
}
return x
}