你设计的解决方案必须 不修改 原数组,且只用常量级 O(1)
的额外空间。
进阶:
- 如何证明
nums
中至少存在一个重复的数字? - 你可以设计一个线性级时间复杂度
O(n)
的解决方案吗?
寻找重复数 - 二分
定义 表示 nums
数组中小于等于 i
的数有多少个,假设重复的数是 target
,那么 里的所有数满足 , 里的所有数满足 ,具有单调性,可以二分
func findDuplicate(nums []int) int {
n := len(nums)
left, right := 0, n - 1
ans := -1
for left <= right {
mid := left + (right - left) >> 1
count := 0
for _, num := range nums {
if num <= mid {
count++
}
}
if count <= mid {
left = mid + 1
} else {
right = mid - 1
ans = mid
}
}
return ans
}
寻找重复数 - 二进制
将所有数二进制展开按位考虑如何找出重复的数,如果能确定重复数每一位是 1
还是 0
就可以按位还原出重复的数是什么
考虑第 i
位,记数组中二进制展开后第 i
位为 1
的数有 个,数字 这 n
个数二进制展开后第 i
位为 1
的数有 y
个,那么当且仅当 时,重复的数第 i
位为 1
func findDuplicate(nums []int) int {
n := len(nums)
ans := 0
for bit := 0; bit < 32; bit++ {
x, y := 0, 0
for i := 0; i < n; i++ {
if nums[i] & (1 << bit) != 0 {
x++
}
if i > 0 && i & (1 << bit) != 0 {
y++
}
}
if x > y {
ans |= 1 << bit
}
}
return ans
}
寻找重复数 - 快慢指针
预备知识
本方法需要对「Floyd 判圈算法」(又称龟兔赛跑算法)有所了解,它是一个检测链表是否有环的算法,相关例题有 环形链表
思路和算法
对数组建图,每个位置 i
连一条 的边。由于存在重复的数字 target,因此 target 这个位置一定有起码两条指向它的边,因此整张图一定存在环,且要找到的 target 就是这个环的入口,那么整个问题就等价于 142.环形链表 II
func findDuplicate(nums []int) int {
slow, fast := 0, 0
for slow, fast = nums[slow], nums[nums[fast]]; slow != fast; slow, fast = nums[slow], nums[nums[fast]] { }
slow = 0
for ; slow != fast; slow, fast = nums[slow], nums[fast] { }
return slow
}