赞
踩
题目链接:leetcode287
二分重复的数的答案,根据抽屉原理,数据中必有重复的数
时间复杂度: O ( n l o g n ) O(nlogn) O(nlogn) ,空间复杂度: O ( 1 ) O(1) O(1)
构造一个快慢指针,第一次相遇后,继续走c步到达环的起点也就是入度大于2的点,即重复的数。
证明:
设环之前的长度为a,环起点到相遇点长度为b,相遇点到环起点为c
由于起初步长相差1,所以直到相遇时,快指针路程一定只比慢指针多k(b+c),也就是环的长度的整数倍
并且两者的路程是两倍的关系,故:2a+2b=a+b+k(b+c)
其中起点也就是入度大于1的点设为长度为0的位置,顺时针走记为+,逆时针记为-
式子恒等变换:a=(k-1)b+kc=kb+(k+1)c=k(b+c)+c
所以从式子可以看出来,另外一个指针从最初起点到环起点走的距离相当于另一个指针在相遇的位置绕环走k圈并多走一个c,两者恰好会再相遇于环起点,证毕!
时间复杂度: O ( n ) O(n) O(n) ,空间复杂度: O ( 1 ) O(1) O(1)
int findDuplicate(int* nums, int n){
int l = 1, r = n, mid;
int cnt;
while (l <= r) {
mid = l + (r - l >> 1);
//judge
cnt = 0;
for (int i = 0; i < n; i ++) {
if (nums[i] <= mid) {
cnt ++;
}
}
//binary search
if (cnt > mid) {
r = mid - 1;
} else {
l = mid + 1;
}
}
return l;
}
int findDuplicate(int* nums, int n){
//i->nums[i] n个点 n+1条有向边必产生环
int fast = 0, slow = 0;
do {
fast = nums[nums[fast]];
slow = nums[slow];
}while (fast != slow); //第一次相遇不一定是入度大于1的点
fast = 0;
do {
fast = nums[fast];
slow = nums[slow];
} while (fast != slow); //继续走c步
return slow;
}
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。