Given an array of integers nums
containing n + 1
integers where each integer is in the range [1, n]
inclusive.
There is only one repeated number in nums
, return this repeated number.
You must solve the problem without modifying the array nums
and uses only constant extra space.
Example 1:
Input: nums = [1,3,4,2,2] Output: 2
Example 2:
Input: nums = [3,1,3,4,2] Output: 3
Example 3:
Input: nums = [1,1] Output: 1
Example 4:
Input: nums = [1,1,2] Output: 1
Constraints:
1 <= n <= 105
nums.length == n + 1
1 <= nums[i] <= n
nums
appear only once except for precisely one integer which appears two or more times.1 class Solution { 2 public: 3 int findDuplicate(vector<int>& nums) { 4 int fast = 0, slow = 0; 5 while(true) { 6 slow = nums[slow]; 7 fast = nums[nums[fast]]; 8 if(slow == fast) break; 9 } 10 fast = 0; 11 while(true) { 12 slow = nums[slow]; 13 fast = nums[fast]; 14 if(slow == fast) break; 15 } 16 return slow; 17 } 18 };
先上代码,这个精简的解法相信大家在各种解析评论里已经见烂了。其核心是使用Floyd判圈算法(Floyd Cycle Detection Algorithm),又称龟兔赛跑算法(Tortoise and Hare Algorithm)来找到环的入口,即那个重复的数。网友 在线疯狂 的 参考解法 里的解析已经比较详细了,但是理解起来还是稍微有点难度的(起码对于我是这样TAT...看了好几遍才看懂)。下面给出一个带图的详细解析作为补充,希望能让大家更好理解Floyd判圈算法背后的逻辑原理以及这道题目是如何和该算法挂钩的。不得不感慨一下这道题的设计,真是无巧不成题呀!
题目告诉我们,数组中的元素范围是 [1, n] ,在该范围内如果没有重复,那应当有 n 个元素;但是给定的数组却有 n+1 个元素,这很明显说明在这些数中有且仅有一个数是重复的,即数组中的元素是由1到n范围内的所有正整数和重复的那个数构成的。对于一个有 n+1 个元素的数组,它的下标取值范围是 [0, n],等等!这里是不是觉得有点意思:给定数组下标范围是[0, n],数组中元素取值范围是[1, n] 。什么意思呢?我们可以巧妙地运用下标和元素的值来访问数组。具体来说,我们可以把数组的每一项看作是一个“任意门”,把当前位置的元素值当作是下一个要传送到的位置的下标值,比如 nums[3] = 4 就表示我们将要被传送到下标为4的位置,然后再根据 nums[4] 中存储的值决定下一次被传送的位置;进一步思考,倘若数组中有重复的元素,假设为k,那么将会有两个“任意门”可以到达 nums[k] ;把这两扇“任意门”合并成一个,是不是就能形成一个环路了呢?nums[k] 就是这个环路的入口。而对于 nums[0] 这个位置,因为数组中的元素都是 >= 1的数,因此没有任何一个“任意门”可以到达 nums[0] 位置,也就是说 nums[0] 到 nums[k] 之间是一个直线型链条,nums[0] 位置就是这个链条的起点。
话不多说直接举个栗子