寻找重复数
题目描述
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
来源:力扣(LeetCode) 链接:https://leetcode-cn.com/problems/find-the-duplicate-number 著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O(n2) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
思路
思路一:
快慢指针。以数组下标k为点,以k->nums[k]为边,做有向图,因为至少存在一个重复的整数,因此该有向图中一定存在环,且该重复数是环的入口。
问题成为寻找有向图中的环的入口节点。[剑指offer-链表中环的入口结点]
code
func findDuplicate(nums []int) int {
f, s := 0, 0
for true {
f = nums[nums[f]]
s = nums[s]
if f == s {
break
}
}
s = 0
for true {
f = nums[f]
s = nums[s]
if f == s {
break
}
}
return f
}
思路二:
二分查找。对于1-n中的数k,在数组中统计≤k的数的个数
- 如果统计cnt<=k,那么表示重复的数大于k
- 如果统计cnt>k,那么表示重复的数小于等于k
假设长度为n+1的数组中,只有一个重复的数,如果cnt>k,则表明k或k之前的数字出现重复了;如果cnt=k,则表明1-k恰好出现一次,因此重复数字出现在k之后;如果cnt<k,则表明1-k中有数字出现空缺,因此重复数字出现在k之后。
code
func findDuplicate(nums []int) int {
l, r := 1, len(nums)
for l < r {
m := (r-l)/2 + l
cnt := 0
for i := 0; i < len(nums); i++ {
if nums[i] <= m {
cnt++
}
}
if cnt <= m {
l = m + 1
} else {
r = m
}
}
return r
}