今天东坝小胖子又出动了,带着他那灿烂的微笑,谜一般的乘着日落里的东风而去,还是变态跳舞的公司。
今天他要做一道这样的题:
给定一个包含
n + 1个整数的数组nums,其数字都在[1, n]范围内(包括1和n),可知至少存在一个重复的整数。假设
nums只有 一个重复的整数 ,返回 这个重复的数 。你设计的解决方案必须 不修改 数组
nums且只用常量级O(1)的额外空间。
小胖仔一看,挑了挑眉毛,直骂娘。这最后一句多余的话是把路全堵死了啊!总不能暴力搞起吧。
不过好在还有一点希望,题目有暗示数字范围跟数组长度的关系,即其数字都在 [1, n] 范围内,这相当于变相的告诉有序。东坝小胖仔急中生智,搜了一圈八股中的算法,灵光一现感觉二分有可为。于是心里演算了一下开始在刷刷的敲起键盘。
func findDuplicate(nums []int) int {
result := -1
left, right := 1, len(nums)-1
for left <= right {
mid := (left + right) / 2
count := 0
for _, num := range nums {
if num <= mid { // 比当前值小的数有多少个?
count++
}
}
if count > mid { // (< mid)的有重复
right = mid - 1
result = mid
} else { // (> mid)的有重复
left = mid + 1
}
}
return result
}
时间复杂度外面的二分logN,每一次都要遍历nums,所以总时间复杂度为O(NlogN),空间复杂度O(1)。
不过小胖子知道这个复杂度显然是得不到满意的,于是该来的还是来了,还能优化吗?小胖子被问道。
“能肯定是能的”,小胖仔回复到,”但是我想不出“,小胖仔摆出一副努力思考后的愁容。
“考虑到值和数组大小的关系,你可以试试把下标i和i对应的值建立映射,然后把它们串起来,这样你可以形成一个链表”
小胖仔听着一拍大腿,秒,不过这神奇的思路,没有提示估计他这种猪脑一辈子也想不出来。小胖仔在纸上演算图画了一下: i -> f(i),把它们连起来: 0->f(0)->i->f(i)->x->f(x)->m->f(m)->y->f(y)->.....
如果有一个重复的数,假设为x、y位置,那么必然存在f(y)->[0...x],所以这道题可以转换为链表环路的问题,小胖仔突然庆幸刷过这代题,于是开始唰唰唰敲起来。
func findDuplicate(nums []int) int {
slow, fast := nums[0], nums[nums[0]]
for slow != fast { // 找到环
slow, fast = nums[slow], nums[nums[fast]]
}
slow = 0
for slow != fast { // 找到入环的位置
slow = nums[slow]
fast = nums[fast]
}
return slow
}