题目简介:
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
1 | 输入: [1,3,4,2,2] |
示例 2:
1 | 输入: [3,1,3,4,2] |
说明:
- 不能更改原数组(假设数组是只读的)。
- 只能使用额外的 O(1) 的空间。
- 时间复杂度小于 O($n^{2}$) 。
- 数组中只有一个重复的数字,但它可能不止重复出现一次。
思路:
由于说明中的要求过多,所以不能按照普通的方法来做,所以想到使用二分的方法。
每次先算出中位数mid
,然后求原始数组中小于等于mid
的个数,如果数量count
超过了mid
,则说明重复的数字出现在[left, mid]
中,否则,则出现在[mid + 1, right]
中。
以 [2, 4, 5, 2, 3, 1, 6, 7]
为例,一共 8
个数,n + 1 = 8
,n = 7
,根据题目意思,每个数都在 1
和 7
之间。
例如:区间[1, 7]
的中位数是 4
,遍历整个数组,统计小于等于 4
的数字的个数,若[1, 4]
区间内不会重复,则该区间内数字的个数最多为 4 个(如[1,2,3,4]
)。如果整个数组里小于等于 4
的整数的个数大于 4 个,就说明重复的数存在于区间 [1, 4]
。然后使right = mid
,进行下一轮。
这个例子中,小于等于 4 的个数为 5 个,令right = 4
,开始第二轮,新区间为[1, 4]
。
第二轮时,中位数mid = 2
,小于等于2
的个数为 3 个,大于mid = 2
,所以令right = 2
,开始第三轮,新区间为[1, 2]
。
第三轮时,中位数mid = 1
,小于等于1
的个数为 1 个,等于mid
,所以令left = mid + 1 = 2
,此时left == right
,返回left == 2
即为重复的数字。
tip:
- 如果整个数组里小于等于
mid
的数字的个数等于mid
,在区间[left, mid]
也不会有重复数字,如上述例子中,若小于等于4
的数字的个数为 4 个且存在重复([1,2,4,4]
),由于数组的总长度为 8 ,且数字只能出现在[1, 7]
内,所以剩余的数字[5, 6, 7]
是不可能有8 - 4 = 4
个的,不然必会有第二个数字重复,与题意不符。
代码如下:
1 | class Solution { |