287. Find the Duplicate Number***
来源:互联网 发布:好用的自动铅笔 知乎 编辑:程序博客网 时间:2024/05/21 04:44
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
方法一、哈希表法
(1)复杂度:时间 O(N) 空间 O(N)
(2)思路:遍历数组时,用一个集合记录已经遍历过的数,如果集合中已经有了说明是重复。但这样要空间,不符合。
方法二、暴力法
(1)复杂度:时间 O(N^2) 空间 O(1)
(2)思路:如果不用空间的话,最直接的方法就是选择一个数,然后再遍历整个数组看是否有跟这个数相同的数就行了。
方法三、排序法
(1)复杂度:时间 O(NlogN) 空间 O(1)
(2)思路:更有效的方法是对数组排序,这样遍历时遇到前后相同的数便是重复,但这样要修改原数组,不符合要求。
方法四、映射找还法
(1)复杂度:时间 O(N) 空间 O(1)
(2)思想:类似于Linked List Cycle II一样寻找单链表环的起始点
假设数组中没有重复,那我们可以做到这么一点,就是将数组的下标和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开始,这两个下标都继续每轮映射一次,当这两个下标相遇时,就是环的起点,也就是重复的数。对这个找环起点算法不懂的,请参考Floyd's Algorithm。
int findDuplicate(vector<int>& nums) { //像寻找单链表的环入口点的思路一样,设置一块一慢的指针,由于数组中存在重复节点,则可通过下标来联想成链表 int len = nums.size(); if(len == 1 || len == 0) return -1; int slow = nums[0]; int fast = nums[slow]; //以下为求两个指针第一次相遇的点 while(slow != fast) { slow = nums[slow]; fast = nums[nums[fast]]; } //以下求重复元素的节点,即重复元素的入口节点 fast = 0; while(fast != slow) { fast = nums[fast]; slow = nums[slow]; } return slow; }方法五、二分法
(1)复杂度:时间 O(NlogN) 空间 O(1)
(2)思路:诸如:二分查找,思想利用下标来实现,诸如:1...10的数中,如果小于5的数目大于5,那么表明重复的数字一定在小于5的数目中,妙哉. 实际上,我们可以根据抽屉原理简化刚才的暴力法。我们不一定要依次选择数,然后看是否有这个数的重复数,我们可以用二分法先选取n/2,按照抽屉原理,整个数组中如果小于等于n/2的数的数量大于n/2,说明1到n/2这个区间是肯定有重复数字的。比如6个抽屉,如果有7个袜子要放到抽屉里,那肯定有一个抽屉至少两个袜子。这里抽屉就是1到n/2的每一个数,而袜子就是整个数组中小于等于n/2的那些数。这样我们就能知道下次选择的数的范围,如果1到n/2区间内肯定有重复数字,则下次在1到n/2范围内找,否则在n/2到n范围内找。下次找的时候,还是找一半。
int findDuplicate(vector<int>& nums) { //二分查找,思想利用下标来实现,诸如:1...10的数中,如果小于5的数目大于5,那么表明重复的数字一定在小于5的数目中,妙哉 int len = nums.size(); if(len == 0 || len == 1) return -1; int low = 0; int high = len - 1; while(low <= high) { int mid = low + (high - low) / 2; int count = 0; for(int i = 0; i < len; i++) if(nums[i] <= mid) count++; if(count > mid) high = mid - 1; else low = mid + 1; } return low; }
0 0
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287.Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- 287. Find the Duplicate Number
- JavaScript设计模式——单例模式
- 工具类库系列(二)-ExePath
- metasploit学习笔记之主机探测和端口扫描
- JSP与servlet之间的传值方式
- 设计模式-----命令模式
- 287. Find the Duplicate Number***
- Final Assignment
- layout-入门
- 百度查询复制粘贴: MySQL 中取子串函数 SUBSTR(SUBSTRING)
- 数组字符串那些经典算法:最大子序列和,最长递增子序列,最长公共子串,最长公共子序列,字符串编辑距离,最长不重复子串,最长回文子串
- 泛化、依赖、关联、聚合、组合
- Android图片加载神器之Fresco,基于各种使用场景的讲解
- Android studio依赖包统一管理
- 在Ubuntu14.04上快速部署OpenStack