最后更新于
最后更新于
给定一个包含 n + 1 个整数的数组 nums,其数字都在 1 到 n 之间(包括 1 和 n),可知至少存在一个重复的整数。假设只有一个重复的整数,找出这个重复的数。
示例 1:
示例 2:
说明:
不能更改原数组(假设数组是只读的)。
只能使用额外的 O(1) 的空间。
时间复杂度小于 O(n2) 。
数组中只有一个重复的数字,但它可能不止重复出现一次。
我们将题目给的数组以链表的角度来看, 链表的头是0, 代表指向数组的首位, 得到这个值num[0]
之后, 下个数就是索引为num[0]
对应的数组中的数num[num[0]]
, 依次类推, 串成一条链. 有因为数组中肯定有重复数, 重复的数都会指向同一个位置, 在第二次遇到这个重复数时, 继续向下就会遇到首次遇到这个重复数的下一个数, 从而形成了环, 之后就会重复下去.
例如数组[1,2,3,4,5,6,7,8,9,5]
, 循环下去就会得到1 2 3 4 5 [6 7 8 9] [6 7 8 9] [6 7 8 9]...
这样的路径.
这样我们就从原题目中抽象出一个链表的构建方法, 接下来我们就要考虑的是判断这个链表有没有环, 如果有环, 找到这个环的起始点, 就找到了重复的数字, 因为重复的数字就在这个环起始点的前一个位置.
二分法常常可以用于限定范围内的一个整数. 这道题要求我们查找的数是一个整数, 并且给出了这个整数的范围, 在1
到n
之间, 并且给出了一些限制, 于是可以使用二分查找法定位在一个区间里的整数.
二分法需要借助一个单调递增或递减的区间, 我们需要寻找这个空间. 对于任何一个数mid
, 统计原始数组中小于等于这个中间数的元素的个数, 记为cnt, 如果cnt
大于mid
, 说明这个重复的数字在[left, mid]之间; 如果cnt
不大于mid
, 说明重复的数字在[mid+1, right]之间.
如果限定空间复杂度为, 就不能使用哈希表的思路. 再加上不能改变原数组, 堆排序的方法也就不可用了.
这样问题就变成了, 在快慢指针第一次相遇后, 将其中一个指针重置为链表的首位, 即0, 然后两个指针以相同的速度每次后移一位, 直到对应的值相等, 这样我们就找到了这个重复值.
我们要做的就是用二分法不断的缩小区间范围, 每次对于所选的mid
数字, 遍历整个原始数组, 统计原始数组中小于等于这个中间数的元素的个数, 决定下一步的区间选择策略. 整体的时间复杂度为.