数组中重复的数
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
思路
1、本人最初的思路
将每一个数与其后的所有数相比,如果相等则返回重复的数
时间复杂度:O(n的平方),空间复杂度:O(1)
public boolean duplicate(int numbers[],int length,int [] duplication) {
int i;
int j;
boolean flag=false;
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return flag;
}
for( i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return flag;
}
}
for(i=0;i<length;i++){
for(j=length-1;j>i;j--){ //从最后的元素遍历
if(numbers[j]==numbers[i]){
break;
}
}
if(j>i){
duplication[0]=numbers[i];
flag= true;
break;
}
}
return flag;
}
public boolean duplicate(int numbers[],int length,int [] duplication) {
int i;
int j;
boolean flag=false;
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return flag;
}
for(i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return flag;
}
}
for(i=0;i<length-1;i++){
for(j=i+1;j<length;j++){ //从下一个元素开始遍历
if(numbers[j]==numbers[i]){
duplication[0]=numbers[i];
flag= true;
break;
}
}
if(j<length){
break;
}
}
return flag;
}
实际上效率较低,代码质量不佳
2、字符串连接
将所有的数组元素连接成一个字符串,然后遍历字符串,如果indexOf和lastIndexOf的位置不同则存在重复
时间复杂度:O(n),空间复杂度:O(1)
public boolean duplicate(int numbers[],int length,int [] duplication) {
StringBuffer str=new StringBuffer();
for(int i=0;i<length;i++){
str.append(numbers[i]+"");
}
for(int j=0;j<length;j++){
if(str.indexOf(numbers[j]+"")!=str.lastIndexOf(numbers[j]+"")){
duplication[0]=numbers[j];
return true;
}
}
return false;
}
虽然通过校验,但是仍然存在一些问题,例如序列长度为12,{0,1,2,3,4,5,6,7,8,9,10,11}
连接成的字符串为01234567891011,str.indexOf(0)==0,str.lastIndexOf(0)==11程序返回有重复,但是实际上却无重复
3、排序:
将数组排序,然后扫描数组中前后相连的元素,相同即有重复
时间复杂度为:O(n的平方),空间复杂度为:O(1)
public class Solution {
//冒泡排序1
public static int[] Sort(int array[]){
int temp;
if(array.length<=1){
return array;
}
else{
//-从前往后冒泡
for(int i=0;i<array.length-1;i++){
for(int j=0;j<array.length-i-1;j++){
if(array[j]>array[j+1]){
temp=array[j];
array[j]=array[j+1];
array[j+1]=temp;
}
}
}
//-从后往前冒泡
for(int i=0;i<array.length-1;i++){
for(int j=array.length-1;j>i;j--){
if(array[j]<array[j-1]){
temp=array[j];
array[j]=array[j-1];
array[j-1]=temp;
}//if
}//j
}//i
}//else
return array;
}
//验证函数
public boolean duplicate(int numbers[],int length,int [] duplication) {
boolean flag=false;
int []array=new int[length];
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return flag;
}
for(int i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return flag;
}
}
array=Sort(numbers);
if(array.length<=1){
duplication[0] = -1;
}
else{
for(int j=0;j<array.length-1;j++){
if(array[j]==array[j+1]){
duplication[0] = array[j];
flag = true;
break;
}
}
}
return flag;
}
}
4.哈希表
从头开始遍历,每遇到一个元素就花费O(1)的时间查询哈希表,查不到就插入表中,查到就返回重复的数。
时间复杂度:O(n) 空间复杂度:O(n)
1.
import java.util.Map;
import java.util.HashMap;
public boolean duplicate(int numbers[],int length,int [] duplication) {
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return false;
}
for(int i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return false;
}
}
HashMap<Integer,Boolean> hm=new HashMap<>();
for(int i=0;i<length;i++){
if(hm.containsKey(numbers[i])){
duplication[0]=numbers[i];
return true;
}else{
hm.put(numbers[i],true);//插入表中
}
}
return false;
}
}
}
import java.util.HashMap;
import java.util.Set;
public class Solution {
public boolean duplicate(int numbers[], int length, int[] duplication) {
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return false;
}
for(int i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return false;
}
}
HashMap <Integer, Integer>map = new HashMap<Integer, Integer>();
//hash映射
for (int i = 0; i < length; i++) {
if (map.containsKey(numbers[i])) {
int temp = map.get(numbers[i]);
map.put(numbers[i], ++temp);
} else
map.put(numbers[i], 1);
}
//找到第一个重复的 return
Set<Integer> set = map.keySet();
for(int i : set) {
if (map.get(i) >= 2 ) {
duplication[0] = i;
return true;
}
}
return false;
}
}
5.交换
0~n-1正常的排序应该是A[i]=i;因此可以通过交换的方式,将它们都各自放回属于自己的位置;
从头到尾扫描数组A,当扫描到下标为i的数字m时,首先比较这个数字m是不是等于i,
如果是,则继续扫描下一个数字;
如果不是,则判断它和A[m]是否相等,如果是,则找到了第一个重复的数字(在下标为i和m的位置都出现了m);如果不是,则把A[i]和A[m]交换,即把m放回属于它的位置;
重复上述过程,直至找到一个重复的数字;
时间复杂度:O(n),空间复杂度:O(1)—所有的操作步骤都是在输入数组上进行的,不需要额外分配空间
(将每个数字放到属于自己的位置最多交换两次)
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
int temp;
boolean flag=false;
if(numbers==null||numbers.length<=0){
duplication[0]=-1;
return flag;
}
for(int i=0;i<numbers.length;i++){
if(numbers[i]<0||numbers[i]>=numbers.length){
duplication[0]=-1;
return flag;
}
}
for(int j=0;j<numbers.length;j++){
while(numbers[j]!=j){
if(numbers[j]==numbers[numbers[j]]){
duplication[0] = numbers[j];
flag = true;
return flag;
}
temp=numbers[j];
numbers[j]=numbers[temp];
numbers[temp]=temp;
}
}
return flag;
}
}
6.Set O(n),O(n)
public int findDuplicate(int[] nums) {
Set<Integer> seen = new HashSet<Integer>();
for (int num : nums) {
if (seen.contains(num)) {
return num;
}
seen.add(num);
}
return -1;
}
题目2----不修改数组
环检测算法(Floyd’s Tortoise and Hare)
-
分析:当兔子和乌龟在环形跑道上跑步时,在某一时刻,兔子会追上乌龟
-
两个步骤,第一个步骤是确定链表是否有环(也就是重复的数字),第二个步骤是确定环的入口点(重复的数字)在哪里。
1.首先,我们初始化两个指针,一个快速的 hare 指针,一个慢的Tortoise 指针。让hare 一次走两个节点,Tortoise 一个节点。最终,Tortoise 和hare 总会在相同的节点相遇,这样就可以证明是否有环。
2.我们初始化两个指针:ptr1指向列表头部的指针,指向ptr2交叉点的指针 。然后,我们把他们每个每次前进1个节点,直到他们相遇; 他们相遇的节点是环的入口,我们就可以得出结果。 -
时间复杂度O(n)空间复杂度O(1)
public int findDuplicate(int[] nums) {
// Find the intersection point of the two runners.
int tortoise = nums[0];
int hare = nums[0];
do {
tortoise = nums[tortoise];
hare = nums[nums[hare]];
} while (tortoise != hare);
// Find the "entrance" to the cycle.
int ptr1 = nums[0];
int ptr2 = tortoise;
while (ptr1 != ptr2) {
ptr1 = nums[ptr1];
ptr2 = nums[ptr2];
}
return ptr1;
}