数据结构中围绕数组、字符串、链表、树、栈以及队列,会有大量的面试题。
此题是关于数组的问题,另外,根据不同的具体要求,此题有多种考法,具体代码实现也会不同。
牛客网上的题目 :
在一个长度为n的数组里的所有数字都在0到n-1的范围内。
数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。
请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
《剑指offer》上根据不同的具体要求,出了两个题目,其中第一个题目与牛客网上的相同。如下:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。
数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。
例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
简单理解就是判断某个长度为n的数组中,是否有重复的数字。
根据牛客网上的提示代码:
public boolean duplicate(int numbers[],int length,int [] duplication)
是要求 判断值返回,并把第一个重复的数字放到duplication[0]中。
我个人的做法是:
1 public static boolean duplicate(int numbers[],int length,int [] duplication) { 2 if(numbers==null||numbers.length==0){ 3 duplication[0] = -1; 4 return false; 5 } 6 ArrayList<Integer> arr = new ArrayList<Integer>(); 7 //Arrays.sort(numbers); 8 //System.out.println(Arrays.toString(numbers)); 9 for(int i:numbers){ 10 arr.add(i); 11 } 12 //Iterator<Integer> it = arr.iterator(); 13 /*while(it.hasNext()){ 14 System.out.print(it.next()+" "); 15 }*/ 16 for(int i:numbers){ 17 if(arr.contains(i)){ 18 19 //it.remove(); 20 int j = arr.indexOf(i); 21 arr.remove(j);//根据下标 去除对应的下标 22 //System.out.println(arr); 23 if(arr.contains(i)){ 24 duplication[0]=i; 25 System.out.println(i); 26 return true; 27 } 28 } 29 } 30 return false; 31 }
主要思路是:①先判断是否输入的数组,是否为空或者长度为0,如果是,直接返回false。如果不是,继续执行。
②定义一个ArrayList arr,用一个for循环将numbers中的值,add(i)到arr中。
③再for循环numbers数组,用arr.contains(i)判断是否包含对应的数字,如果包含,删除对应数字,再进行arr.contains(i),如果还包含,就说明此数字重复,返回true即可。
虽然结果是对的,但是由于另外创建了ArrayList,导致空间复杂度为O(n)。
根据《剑指offer》上提出的思路,实现下面代码,使得空间复杂度为O(1):
1 /* 2 * 交换数组内的数字顺序 3 */ 4 public static boolean duplicate2(int numbers[],int length,int [] duplication) { 5 if(numbers==null||numbers.length==0){ 6 duplication[0] = -1; 7 return false; 8 } 9 for(int i = 0;i<length;i++){ 10 while(numbers[i]!=i) 11 { 12 if(numbers[i]==numbers[numbers[i]]){ 13 duplication[0] = numbers[i]; 14 System.out.println(duplication[0]); 15 return true; 16 } 17 int temp = numbers[i]; 18 numbers[i] = numbers[temp]; 19 numbers[temp] = temp; 20 } 21 } 22 return false; 23 }
主要思路是:①先判断输入的数组是否为空,或者数组长度为0。
②让数组内的数字和数组的下标之间产生关联,即如果数组内的数字与当前数字的数组下标不相等,就将数组内的数字,交换到对应数字下标的位置,例如数组内numbers[0]的值是2,2不等于0,则将2与numbers[2]的交换。
③在数组内的数字与当前数字的数组下标不相等的情况下,如果出现,当前数字与即将交换位置的数字相等,则说明当前数字重复,返回true。例如,如果numbers[0]为2,2不等于0,则判断2是否等于numbers[2],如果numbers[2]为2,则说明2是重复的,结束。
另外,如果将题目改一下,限制一下数组里数字的范围,就是《剑指offer》上这道题的第二个题目:
在一个长度为n+1的数组里的所有数字都在1到n的范围内。
数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。
请找出数组中任意一个重复的数字。 例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2or3。
根据书上给出的思路,自己实现的代码:
1 public static boolean duplicate3(int[] numbers, int length, int[] duplication) { 2 if(numbers==null||numbers.length==0){ 3 duplication[0] = -1; 4 return false; 5 } 6 //int start = 1,end = length-1; 7 int start = 1,end = length-1; 8 while(end >= start){ 9 //int middle = ((end-start)>>1)+start; 10 int middle = (end-start)/2 + start; 11 int count = countRange(numbers,length,start,middle); 12 if(start==end){ 13 if(count>1){ 14 duplication[0] = start; 15 System.out.println(start); 16 return true; 17 }else{ 18 duplication[0] = -1; 19 System.out.println(start); 20 return false; 21 } 22 } 23 24 if(count>middle){ 25 end = middle; 26 }else{ 27 start = middle+1; 28 } 29 } 30 return false; 31 } 32 public static int countRange(int[] numbers, int length, int start, int end) { 33 int count=0; 34 for(int i=0;i<length;i++){ 35 if(numbers[i]>=start && numbers[i]<=end){ 36 count++; 37 } 38 } 39 return count; 40 41 }
用的是类似于二分法的思路,例如在1-3的范围中,如果小于3和大于1的数字的个数count,大于3,说明这个范围的数字是有重复的,然后进一步缩小范围,然后再进行计数,直到start==end的
范围中,出现count>1的,即是重复的数。
这种算法也是有缺陷的,比如在1-2范围内,有两个2,此时不能确定是每个数字各出现一次,还是某个数字出现两次。