找出数组中重复的数字。
在一个长度为 n 的数组 nums 里的所有数字都在 0~n-1 的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。示例 1:
输入:
[2, 3, 1, 0, 2, 5, 3]
输出:2 或 3
题解:
题解1:Set
Set是元素无序,不可重复的集合
使用HashSet来解决这道题目
遍历整个数组,将数组中的元素存放到Set中,在存放前先判断该元素是否在Set中,若存在则直接返回该元素
public int findReatNumber(int[] nums){
Set set = new HashSet<Integer>();
for(int i :nums){
if(set.contains(i)){
return i;
}
else{
set.add(i);
}
}
return -1;
}
题解2:
数组存放的数字范围是在0~n; 所以可以将数组进行排序,将元素归位到和下标对应相等的位置(即一个萝卜一个坑)如果这个萝卜对应的坑位已被占用,说明存在重复。则输出该元素。
public int findReatNumber1(int[] nums){
int temp;
for(int i =0;i<nums.length;i++){
while(i!=nums[i]){
if(nums[i]==nums[nums[i]]){
return nums[i];
}
temp = nums[i];
nums[i]=nums[temp];
nums[temp]=temp;
}
}
return -1;
}
题解3:快慢指针
题目可以转化成142. 环形链表 II来做
将数组转化成一个链表,起点为第0个元素值,下一个结点为当前元素作为索引值对应的元素
由于有重复的元素,那么当遍历到重复的元素后必定形成循环。
nums[i] | 2 | 3 | 1 | 4 | 5 | 4 | 7 | 6 |
---|---|---|---|---|---|---|---|---|
i | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
此时生成的链表为
2 --> nums[ 2 ] : 1 -->nums[1] :3 --> nums[3] :4 -->nums[ 4 ] : 5 -->nums[5] :4
^ |
| |
---------------
此时可以使用两个指针 slow 和 fast 分别指向数组第0个元素 nums[0]
环形链表由两部分组成a个结点在环外,b个结点在环内
快指针fast每次走2步 慢指针slow每次走1步,假设slow走了s步后与fast走了f步后相遇, f = 2s
此时fast比slow多走了n个环的步数 即 f = s+nb
两式合并得 s= nb
那么要确定a的位置 则只需要 slow指针再走 a步,就可以回到环的起点
此时可以将fast重置到 head结点,令fast和slow每次只走1步,当他们再次相遇时即是走了a步
最后返回此时的结点
class Solution {
public int findDuplicate(int[] nums) {
int slow = nums[0];
int fast = nums[0];
while(true){
slow=nums[slow];
fast=nums[nums[fast]];
if(slow==fast){
break;
}
}
slow = nums[0];
while(slow!=fast){
slow=nums[slow];
fast=nums[fast];
}
return slow;
}
}