方法1 (二分查找):
例子:
- 我们把(1-12)12个数放在了13个框里,也就是我们最终零号位置填入一个数,也就是重复的那个数,如果我们填入的那个数小于等于6,那么小于等于mid的数由原来的6个数变成了7个数,(如果填入的数大于6,则大于mid的数由原来的6个数变成了7个数),我们可以通过这个特性,来进行二分查找
- 在步骤1中我们只讨论了体会零号位置所造成的变化,但其实改变其他位置所造成的效果也是一样的
- 至于代码实现的细节,外层for循环进行二分控制,临界条件 l <= r
- 内层for循环进行遍历查找小于等于mid的数的个数,记录这个个数(cont),在退出内层for循环后与mid进行比较,如果满足小于等于mid,说明重复的那个数一定,大于mid
- 如果大于mid,则重复的数小于等于mid,但是为了可以退出循环,我们需要将 r=(mid-1),为了防止mid刚好就是那个重复的数,那么我们就需要将mid先记录为ans,如果之后的循环无法再进入else,那么也就说明ans所记录的那个数就是答案,如果再次进入else,那么ans也会被覆盖
func findDuplicate(nums []int) int {
ans := 0
n := len(nums)
l , r := 1 , n-1
for l <= r {
mid := (l+r) >> 1
cont := 0
for i := 0; i < n; i++{
if nums[i] <= mid {
cont++
}
}
if cont <= mid {
l = mid + 1
}else {
r = mid - 1
ans = mid
}
}
return ans
}
方法2(二进制——二分查找):
func findDuplicate(nums []int) int {
n := len(nums)
ans := 0
bit_max := 31
for ((n-1) >> bit_max) == 0 {
bit_max--
}
for bit := 0; bit <= bit_max; bit++ {
x, y := 0, 0
for i := 0; i < n; i++ {
if (nums[i] & (1 << bit)) > 0 {
x++
}
if i >= 1 && (i & (1 << bit)) > 0 {
y++
}
}
if x > y {
ans |= 1 << bit
}
}
return ans
}
方法3(快慢指针):
func findDuplicate(nums []int) int {
slow,fast := 0,0
slow,fast = nums[slow],nums[nums[fast]]
for slow != fast {
slow, fast = nums[slow],nums[nums[fast]]
}
slow = 0
for slow != fast {
slow, fast = nums[slow], nums[fast]
}
return slow
}