51 数组中重复的数字

Posted shareidea94

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了51 数组中重复的数字相关的知识,希望对你有一定的参考价值。

题目要求:在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组2,3,1,0,2,5,3,那么对应的输出是第一个重复的数字2。

 1 public class Solution 
 2     // Parameters:
 3     //    numbers:     an array of integers
 4     //    length:      the length of array numbers
 5     //    duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
 6     //                  Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
 7     //    这里要特别注意~返回任意重复的一个,赋值duplication[0]
 8     // Return value:       true if the input is valid, and there are some duplications in the array number
 9     //                     otherwise false
10     public boolean duplicate(int numbers[],int length,int [] duplication) 
11     
12 

 思路一:最好的解法

此大法利用了哈希的特性,但不需要额外的存储空间。 因此时间复杂度为O(n),不需要额外空间!

因为列表总共有n个元素,所有元素可能取到的元素有0~n-1,共n种。如果不存在重复的数字,那么排序后数字i将会出现在下标为i的位置。现在让我们重新扫描数组,

  • 当扫描到下标为i的数字时,首先比较这个数字(记为m)与i是否相等:
  • 如果是,继续扫描下一个元素,
  • 如果不是,则再拿它与第m个数字比较:
    • 如果它和第m个数字相同,就找到了一个重复的元素;
    • 如果不同,就将m与第m个数字互换。接下来继续重头开始,重复换这个比较。
 1 public class Solution 
 2     public boolean duplicate(int numbers[],int length,int [] duplication) 
 3         //考虑特殊情况,并判断数组是否合法(每个数都在0~n-1之间)
 4         if(length<=0||numbers==null) return false;
 5         for(int i=0;i<length;i++)
 6             if(numbers[i]<=0||numbers[i]>length-1)
 7                 return false;
 8         
 9         //关键代码来了
10         for(int i=0;i<length;i++)
11             while(numbers[i]!=i)
12                 if(numbers[i]==numbers[numbers[i]])
13                     duplication[0] = numbers[i];
14                     return true;
15                 
16                 //交换numbers[i]和numbers[numbers[i]]
17                 int temp = numbers[i];
18                 numbers[i] = numbers[temp];
19                 numbers[temp] = temp;
20             
21         
22         return false;
23     
24 

 居然能找到错误,开森

 

/* 一开始交换写错了写成了这:
int temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[numbers[i]] = temp;
于是就错了...
*/

 思路二:利用HashMap或者HashTable 

不建议

  1. 由于所有元素值是有范围的,因此可以用一个长度为n的数组,下标表示序列中的每一个值,下标对应的值表示该下标出现的次数。
  2. 只需扫描一次原序列,就统计出所有元素出现的次数;
  3. 再扫描一次哈希数组,找到一个出现次数大于1的值即可。

 这种方法时间复杂度和空间复杂度都为O(n)。

 1 public boolean duplicate(int array[],int length,int [] duplication) 
 2     if ( array==null ) return false;
 3 
 4     // 判断数组是否合法(每个数都在0~n-1之间)
 5     for ( int i=0; i<length; i++ ) 
 6         if ( array[i]<0 || array[i]>length-1 ) 
 7             return false;
 8         
 9     
10 
11     // key step
12     int[] hash = new int[length];
13     for( int i=0; i<length; i++ )
14         hash[array[i]]++;
15     
16     for(int i=0; i<length; i++)
17         if ( hash[i]>1 ) 
18             duplication[0] = i;
19             return true;
20         
21     
22     return false;
23 

 

 另外一个相似的题目

 

在一个长度为n+1的数组里的所有数字都在1~n范围内,所以数字中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改数组。例如,如果输入长度为8的数组2,3,5,4,3,2,6,7,那么对应的输出是重复的数字2或3。(n+1个元素,n种可能的取值)

 

最优解法:避免使用O(n)的辅助空间。我们把取值空间[1,n]从中间的数字m分为两部分,前面一部分为1~m,后面一部分为m+1~n。如果数组中元素落在前面一部分的元素个数多于m个,那么数组中重复的元素一定落在前半区间;否则,数组中重复的元素一定落在后半区间。然后,我们可以继续将包含重复元素的区间一分为二,直到找到一个重复的元素。

 

以上是关于51 数组中重复的数字的主要内容,如果未能解决你的问题,请参考以下文章

51 数组中重复的数字

8.剑指Offer --- 英文版新增面试题

Java数组打印出奇怪的数字和文本[重复]

给定一个只包含正整数的非空数组,返回该数组中重复次数最多的前N个数字 ,返回的结果按重复次数从多到少降序排列(N不存在取值非法的情况)

数组中重复的数字

剑指 Offer —— 数组中重复的数字