本来以为这题很简单,但是看要求,第一不能改变这个数组,第二只能使用O(1)的空间,第三时间复杂度小于O(n^2),就不能使用遍历数组的方式来解决了。
有两种方法,一种是利用Binary Search,一种是利用Floyd的cycle detection算法。
Binary Search Method
This method is based on a theroy called Pigeonhole princinpal.
In mathematics,Pigeonhole princinpal states that if n items are put into a m container,and n>m,then at least one container must contain more than one item.
思路1:1.At first,the search space is 1~n,we find out the middle of 1~n as mid and figure out how many numbers in the array is equals or less than mid.We define it as count.
2.If count is equals or less than mid,then the search space shrink to mid+1~n.
3.If count is bigger than mid,then the search space shrink to 1~mid.
e.g. Given an array,[1,2,3,4,4],n=4
mid=1+(4-1)/2=2,then we compare all the numbers in the array and get count=2.
There is only two numbers between 1~2,and count=2.It means that there are 2 spaces for (1~2).
You may think that just because there are 2 spaces for 1~2,it doesn‘t mean there are no duplicate numbers between 1~2,it can be {1,1} or {2,2} when count=2.
But think about the rest of the array.
There are two numbers between (1~2),so there are 3 spaces left and the rest of the numbers are between (3~4).
There are only two numbers between 3~4 but there are 3 space for them,so according to the Pigeonhole princinpal, there must be a duplicate number between (3~4).
And we can know from the question that there is only one duplicate number in the array.So the duplicate one must be between (3~4).
Then we keep on searching.mid=3+(4-3)/2=3,then we can conclude that count=3.There are 3 numbers between 1~3 and there are 2 numbers between (4~4).We may conclude that the duplicate number is 4.
public static int findDuplicate(int[] nums) { int low=1; int high=nums.length-1; int count=0; while(low<high) { count=0; int mid=low+(high-low)/2;//先找到中间值 for(int i=0;i<nums.length;i++) {//统计数组中小于等于这个值的有多少个 if(nums[i]<=mid) { count++; } } if(count<=mid) {//如果个数小于等于中间值,说明low~mid之间没有重复的值,重复的值在mid+1~high中 low=mid+1; } else {//如果个数大于中间值,说明low~mid中存在重复的值 high=mid; } } return low; }
Cycle Detection Algorithm by Floyd
How it works
Let‘s have a tortoise and a hare(Two pointers) pointing to the beginning of the list.
First part of the algorithm
They start to move and the tortoise move a step while the hare move two steps each time.
Then they meet at some meeting point.
The figure illustrates a cycle and m states the step from the start to the cycle.
The cycle length is n and the tortoise and the hare first meet at k steps away from the cycle start.
The tortoise move i step,And i=m+p*n+k(Assume that the tortoise move around the cycle for p times).
The hare move 2i step,And 2i=m+q*n+k(Assume that the hare move around the cycle for q times).
i=2i-i=(q-p)*n so that m+k=(q-2p)n
We can conclude that m+k is some multiples of the length of the cycle.
Second part of the algorithm
Let‘s move the tortoise to the start of the list and keep the hare at the meeting point.
Now we start to move them and they both move a step at a time.
The hypothesis is that if we move them at the same speed,then they will finally meet at the beginning of the cycle.
If we move them m+k steps,then the tortoise will be at the previous meeting point as we can see from the picture.
What about the hare?Because m+k is some multiples of n,then the hare also move several cycle length and get to the meeting point.
What if we move them m steps?
The tortoise will be at the beginningg of the cycle,and the hare would go be k steps short of completing (q-2p) rotations.
ince it started k steps in front of the cycle beginning, hare would have to arrive at the cycle beginning.
Obviously m<m+k,so when they first meet,they will get to the beginning of the cycle and we get the result.
public static int findDuplicate(int[] nums) { int fast=0; int slow=0; //算法的第一部分 do { fast=nums[nums[fast]];//fast一次走两步 slow=nums[slow];//slow一次走一步 }while(slow!=fast);//最终他们相遇 //算法的第二部分 int finder=0;//finder设置到起点 while(finder!=fast) {//当他们相遇时,他们所在的点就是cycle的起点 finder=nums[finder]; fast=nums[fast]; } return fast; }