您的位置:首页 > 其它

leetcode——Find the Duplicate Number

2016-07-04 20:53 363 查看
题目:

Given an array nums containing n + 1 integers where each integer is between 1 and
n (inclusive), prove that at least one duplicate number must exist. Assume that there is only one duplicate number, find the duplicate one.

Note:

You must not modify the array (assume the array is read only).
You must use only constant, O(1) extra space.
Your runtime complexity should be less than
O(n2)
.
There is only one duplicate number in the array, but it could be repeated more than once.
方法一:二分法

分析:

        n+1个数属于区间[1, n],必然存在重复(抽屉原则),抽象地,超过(b-a+1)个数属于区间[a, b]必然也存在重复。比较粗鲁的方式是在nums中分别搜索1~n,判断有没有重复,时间复杂度o(n^2)不行。一定需要遍历数组n次么?可不可以少一点呢?

        考察n/2,遍历nums,如果小于等于n/2的数超过n/2,那么区间[1, n/2]中必然存在重复;如果不超过n/2,说明属于区间[n/2+1, n]中的数(n+1-n/2)大于n/2,因此区间[n/2+1, n]中必然存在重复的数。利用这种方式可以折半查找区间,时间复杂度(nlgn)。

class Solution {
public:
int findDuplicate(vector<int>& nums) {
int first = 1, last = nums.size() - 1;//两边都是闭区间
while (first < last) //如果first==last,那么first就是要找的数
{
int mid = first + (last - first) / 2;
//统计小于等于mid的数的个数
int count = 0;
for (auto p : nums)
{
if (p <= mid)
{
++count;
}
}
//分成两个区间[first, n/2]和[n/2+1, last]
if (count > mid) //[1, 2, ..., n/2]共n/2个数,如果count > n/2,那么必然存在重复
{
last = mid;
}
else
{
first = mid + 1;
}
}
return first;
}
};
方法二:链表找环 https://segmentfault.com/a/1190000003817671         假设数组中没有重复,那我们可以做到这么一点,就是将数组的下标和1到n每一个数一对一的映射起来。比如数组是213,则映射关系为0->2, 1->1, 2->3。假设这个一对一映射关系是一个函数f(n),其中n是下标,f(n)是映射到的数。如果我们从下标为0出发,根据这个函数计算出一个值,以这个值为新的下标,再用这个函数计算,以此类推,直到下标超界。实际上可以产生一个类似链表一样的序列。比如在这个例子中有两个下标的序列,0->2->3。

        但如果有重复的话,这中间就会产生多对一的映射,比如数组2131,则映射关系为0->2, {1,3}->1, 2->3。这样,我们推演的序列就一定会有环路了,这里下标的序列是0->2->3->1->1->1->1->...,而环的起点就是重复的数。

        所以该题实际上就是找环路起点的题,和Linked List Cycle II一样。我们先用快慢两个下标都从0开始,快下标每轮映射两次,慢下标每轮映射一次,直到两个下标再次相同。这时候保持慢下标位置不变,再用一个新的下标从0开始,这两个下标都继续每轮映射一次,当这两个下标相遇时,就是环的起点,也就是重复的数。

class Solution {
public:
int findDuplicate(vector<int>& nums) {
int slow = 0, fast = 0;
do
{
slow = nums[slow];
fast = nums[nums[fast]];//每次跨过两个结点
}
while (slow != fast);

//再定义一个慢指针从起点出发,与slow相遇在环的入口
int find = 0;
while (slow != find)
{
slow = nums[slow];
find = nums[find];
}
return find;
}
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  leetcode 二分法