287. Find the Duplicate Number(链表判环)

来源:互联网 发布:郑州淘宝加盟被骗了 编辑:程序博客网 时间:2024/05/16 17:34

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:

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

首先讲一下我的思路:

1.哈希表

对于这种数组中找重复元素的问题,我当然第一时间想到哈希表,但是哈希表空间复杂度是O(n),所以不能用。

2.暴力法

两个循环就可以暴力查找出重复的元素,时间复杂度是O(n^2),题目要求less than O(n^2),所以排除。

4.排序

时间是可以,但会改变数组,所以不可取。

5.二分法

我想到了二分法,但是刚开始我思路走入了误区。一共n+1个元素,每个元素都是在[1..n]范围之内,那么除过一个重复的,剩下的肯定就是无重复的且范围在[1..n]之内的元素了。

况且,这道题二分法和一般的不一样,这道题的思想是这样的。既然有元素值的范围,我们对元素值的范围进行二分,而不是对数组二分,然后通过统计数组中元素个数来加上限制条件,缩小元素值范围继续二分下去。

比如:对于元素集合[1..10],现在有n+1个元素,即10+1=11个,那么必定有一个重复元素。

我们首先将元素集合分为[1..5] 和 [6..10],此刻mid=5。通常来说,如果遍历数组得到<=5的元素数目和mid相等,则说明重复元素在右半区间中。否则,在左半区间中。然后继续二分下去,直到最终只有一个元素。例如:元素4重复了,那么数组中会有1,2,3,4,4,5,一共6个元素,这不正说明重复元素在mid即5的左半区间吗?

由于一直二分下去,最终剩下两个元素的时候,high会向low靠拢,所以我们返回low即可。

代码如下:

class Solution {public:    int findDuplicate(vector<int>& nums) {        size_t size = nums.size();        if(size ==0 || size == 1)            return -1;             return binsearch_like(nums, size, 1, size-1);    }   private:    int binsearch_like(vector<int>& nums, int size, int low, int high){        while(low < high){            int mid = low + (int)((high-low)>>1);            int count = 0;            for(int i=0; i<size; ++i){                if(nums[i] <= mid)                    ++count;            }               if(count > mid)                high = mid;            else                low = mid+1;        }           return low;    }};


4.映射找环法

对于数组a[3] = {1, 2, 3},我们把它看作下标和元素值的映射。0->1, 1->2, 2->3, 连起来就是 0->1->2->3。

既然存在重复元素,例如数组a[5] = {1, 2, 3, 3, 4}, 连起来就是 0->1->2->3->3->3...无限环转圈

或者数组a[6] = {1, 2, 4, 5, 3, 3},连起来就是0->1->2->4->3->5->3->5->3->5->3....同样无线循环转圈

这道题这就转换成了链表求环的问题。

这种题的通常解法就是一个快指针,一个慢指针。快指针步长为满指针的2倍。就好像在操场跑步,一个人跑的快,一个人跑的慢,只要操场是个环,那么跑的快的总会追上跑得慢的。追上的点就是有环点。

由于步长的

然后再定义一个指针从0开始,之前的指针从有环点开始,两个相遇点即为重复元素所在。

代码如下:

class Solution {public:    int findDuplicate(vector<int>& nums) {        size_t size = nums.size();        if(size ==0 || size == 1)            return -1;             int slow = 0;        int fast = 0;        do{            slow = nums[slow];            fast = nums[nums[fast]];        }while(slow != fast);                int find = 0;        while(find != slow){            find = nums[find];            slow = nums[slow];        }        return slow;    }   };

证明如下:

如果fast和slow相遇,那么在相遇时,slow肯定没有遍历完链表,而fast已经在环内循环了n圈(n>=1)(见下面的证明2)。假设 slow走了s步,则fast走了2s步(fast的步数还等于s加上在环上多转的n圈),设环长为r,则: 
2s=s+nr 
s=nr 
设整个链表长L,入口环与相遇点距离为x,起点到环入口点的距离为a,则 
a+x=s=nr 
a+x=(n-1)r+r=(n-1)r+L-a 
a=(n-1)r+(L-a-x) 
(L-a-x)为相遇点到环入口点的距离。由此可知,从链表头到环入口点等于(n-1)循环内环+相遇点到环入口点。于是可以从链表头和相遇点分别设一个 指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。 

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 梅花螺丝滑丝了怎么办 螺丝孔道滑丝了,怎么办 锅把铆钉松啦怎么办 膨胀螺丝孔大了怎么办 戒指不圆了怎么办妙招 眼镜框的腿断了怎么办 弹簧腿眼镜坏了怎么办 vps搭建ss被墙怎么办 注塑机螺杆黏料怎么办 分解师耐久度0了怎么办 有鼻涕擤不出来怎么办 擤鼻涕耳朵堵了怎么办 脸用什么都过敏怎么办 1岁宝宝流清鼻涕怎么办 2岁宝宝流清鼻涕怎么办 3岁儿童流清鼻涕怎么办 鼻子老是流清水鼻涕怎么办 5岁儿童感冒鼻塞怎么办 3岁宝宝感冒鼻塞怎么办 宝宝9个月流鼻涕怎么办 鼻涕又黄又粘稠怎么办 鼻子一直流黄水怎么办 宝宝眼屎多又黄怎么办 宝宝痰多鼻涕多怎么办 小孩痰多鼻涕多怎么办 用qq登不了微博怎么办 中国银行u盾丢了怎么办 我的世界遇见him怎么办 考军校年龄超了怎么办 dnf组队就红电脑怎么办 九阳高压锅漏气怎么办 晋江买了防盗章怎么办 开车撞了人应该怎么办 开车撞了人没钱怎么办 驾照扣分12分后怎么办 车被交警拖走了怎么办 符石耐久没了怎么办 冒险岛2老是掉线怎么办 冒险岛老是掉线怎么办 冒险岛2延迟高怎么办 高速超速扣6分怎么办