题干
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
直觉思路
-
排序 O(nlogn): O(nlogn)排序+O(n)遍历查找
-
哈希表 O(n)+O(n)
分析后才能得到的思路
- 在没有重复数字的情况下,每个位置的数字应该和下标对应相等。
怎么想到的
这个思路怎么想到的,暂时难以清晰描述出来,只能观察题目的特点,进行思路尝试。
PS. 注意区分排序后的位置
和属于它的位置
,假设没有重复,则每个数都有属于自己的位置。 如果有重复的数,则会出现2个数放到同一个位置的情况,也就发现了重复元素。
题目分析
[1(cur),0,1]
cur(1)的位置不对,和位置1的元素交换 -> [0,1,1]
cur(0)的位置正确,cur前进 -> [0,1(cur),1]
cur(1)的位置正确,cur前进 -> [0,1,1(cur)]
cur(1)的位置不对,交换前进行检查,发现目标位置已经有该元素,即此时的cur就是重复数字。
代码编写思路
基于[101]的分析:
从描述中,可以分享有个外循环负责遍历,内部有while循环直到cur位置的元素放在了正确位置才结束,如果不在正确位置就要交换到正确位置,交换位置之前需要检查cur位置和交换位置的值是否相等,相等则找到重复,不相等则交换。
func FindAnyDuplicates(nums []int) int {
// 5. 输入空检查
if len(nums) == 0 || nums == nil {
return -1
}
for _, v := range nums {
if v < 0 || v > len(nums)-1 { // 4.输入合理性检查 数字范围[0,n-1]
return -1
}
}
// 1. 遍历数组
for i, _ := range nums {
// 2. 内循环直到把cur位置放的就是cur值才结束
for nums[i] != i {
// 3. 如果交换的目标位置值就是cur值,说明出现重复
if nums[i] == nums[nums[i]] {
return nums[i]
}
// 3. 把当前元素交换到属于它的位置 // YC: 不是放到排序后的位置 // YC:假设数组没有重复,则cur就被交换到了排序后的位置 // TODO:在有重复元素的情况下,并不是交换到属于他的位置。比如[23344],把2交换位置2[33244],而位置2显然不是排序后2的位置,而是属于2的位置。
nums[i], nums[nums[i]] = nums[nums[i]], nums[i]
}
}
return -1
}
- 时间复杂度
YW: 你自己拿个数组试一试就知道,这种写法,最坏情况,也就是交换两次的时候是:第一次被交换(另一个数回到自己的下标)等下一次在用到它,就该它跟别人换,就到自己数值的那个下标了。
我本来是想用常规思路分析复杂度,先外层O(n),然后内层常数次,但是这个不太好使。
所以2个循环一起分析,因为每个数最多交换2次就能找到对的位置,所以这2个循环整体执行交换操作的次数不超过2n,所以就是O(n)。
速记
每个位置的数字与其对应的下标相等