题目
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。
示例 1:
输入: [2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
第一种解法
一个简单的暴力方法就是先把输入的数组排序.从排序的数组中找出重复的数字是一件容易的事情,只需要判断一个数字的下一个数字是否与本身相等即可
.但是排序一个长度为n的数组需要O(nlogn)的时间
时间复杂度较高,不可取
第二种解法
使用哈希表来解决这个问题.从头到尾按顺序扫描这个数组的每个数字.
每扫描到一个数字的时候.都可以用O(1)的时间来判断哈希表中是否存在了这个数字. 如果哈希表中没有这个数字就把他加入进去,下次再判断时就能找出来
时间复杂度 : O(n)
空间复杂度 : O(n)提高了时间复杂度的前提下是牺牲了一个大小为O(n)的哈希表为代价的
// 使用哈希表来储存数组中数字出现的次数
func findRepeatNumber(nums []int) int{
// 先建造一个map, key为int类型存数组里面的值,value为bool类型存真假
m := make(map[int]bool)
// for range nums 取出值并且判断是否在哈希表里面存在
// 若没有存在那么就是把这个值设为key,value设为true
// 若下次再出现了这个值,那么就可以返回这个值了
for _, num := range nums{
if _,exist := m[num]; exist{
return num
}
m[num] = true
}
return -1
}
第三种解法
有没有一种空间上为O(1)的更高效的解法呢?
在一个长度为 n 的数组 nums 里的所有数字都在 0 ~ n-1 的范围内 。
此说明含义:数组元素的 索引 和 值 是 一对多 的关系。
因此,可遍历数组并通过交换操作,使元素的 索引 与 值 一一对应(即 nums[i] = i )。因而,就能通过索引映射对应的值,起到与字典等价的作用。
遍历中,第一次遇到数字 x 时,将其交换至索引 x 处;而当第二次遇到数字 x 时,一定有 nums[x] = x ,此时即可得到一组重复数字。
算法流程:
遍历数组 nums ,设索引初始值为 i=0 :
若 nums[i]=i : 说明此数字已在对应索引位置,无需交换,因此跳过;
若 nums[nums[i]] = nums[i] : 代表索引 nums[i] 处和索引 ii 处的元素值都为 nums[i] ,即找到一组重复值,返回此值 nums[i] ;
否则: 交换索引为 i 和 nums[i] 的元素值,将此数字交换至对应索引位置。
若遍历完毕尚未返回,则返回 −1 。
func findRepeatNumber2(nums []int) int {
// 效验参数的合法性
if len(nums) <= 0 {
return -1
}
for i := 0;i < len(nums);i++ {
if nums[i] < 0 || nums[i] > len(nums) - 1{
return -1
}
}
var tmp int = 0
for i := 0;i < len(nums);i++ {
for nums[i] != i {
if nums[i] == nums[nums[i]] {
return nums[i]
}
tmp = nums[i]
nums[i] = nums[tmp]
nums[tmp] = tmp
}
}
return -1
}
值得一提的东西
测试用例可能包含无效测试用例
如输入长度为n的数组中包含(0~n-1)之外的数字
传入空数组等等
所以我们需要先进行参数的校验合法性