剑指offer
面试题3:数组中重复的数字
题目描述:
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中第一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字2。
-
数组排序
把输入数组作排序处理,在重头扫描。采用排序,时间复杂度O(nlogn)。
-
哈希表
利用Hashset,表中不能存相同数据的特征。表中如果没有就添加进去,有的话即发现重复的数字。
private static int duplicate(int[] numbers) { for ( int i= 0 ; i<numbers.length; i++) { int index = numbers[i]; if (index >= numbers.length) { index -= numbers.length; } if (numbers[index] >= numbers.length) { return index; } numbers[index] = numbers[index] + numbers.length; } return - 1 ; }
还有一种也类似哈希表的处理方式。
private static int duplicate(int[] numbers) { if (numbers == null || numbers.length <= 0){ return -1; } boolean[] k = new boolean[numbers.length]; for (int i : numbers) { if (i < 0 || i > numbers.length - 1){ return -1; } //如果为true说明已经赋值过,即发现重复数字 if (k[i] == true){ return i; } k[i] = true; } return -1; }
-
空间复杂度为O(1)的算法
从头到尾依次扫描这个数组中的每个数字。当扫描到下标为i的数字,首先比较这个数字(用m来表示)是不是等于i。如果是,则接着扫描下一个数字;如果不是,则再拿它和第m个数字进行比较。
如果它和第m个数字相等,就找到了重复的数字;如果不相等,就把第i个数字和第m个数字交换。接着重复这个比较,交换的过程。
private static int duplicate(int[] numbers) { if (numbers == null || numbers.length <= 0){ return -1; } for (int i : numbers) { if (i < 0 || i > numbers.length - 1){ return -1; } } //从0开始依次比较 for (int i = 0; i < numbers.length; i++) { //当数组当前值不等于数组的下标时 while (numbers[i] != i){ //如果数组值 等于 数组值对应下标的数组值(有点绕),则找到重复值 if (numbers[i] == numbers[numbers[i]]){ return numbers[i]; } //相当于交换swap(array[i],array[array[i]]), { int t = numbers[i]; numbers[i] = numbers[t]; numbers[t] = t; } } } return -1; } }
-
牛客网的一种很牛逼的算法
结合代码看,如果一个数被访问了,设置对应数作为下标即给numbers[index]赋值为numbers[index]+length,当下一次遇到相同数时,numbers[index]会大于length。注意,当访问已经赋值过的数时,即index >= length,要进行 index -= length。非常机智!!!
private static int duplicate(int[] numbers) { for ( int i= 0 ; i<numbers.length; i++) { int index = numbers[i]; if (index >= numbers.length) { index -= numbers.length; } if (numbers[index] >= numbers.length) { return index; } numbers[index] = numbers[index] + numbers.length; } return -1; }