在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
代码
解法一
先排序,这样重复的数字就会前后相邻,然后遍历数组,只要当前数字和下一个数字相等,则说明该数字重复,时间复杂度因排序而定,O(nlogn),但是存在的问题是,如果重复的数量大于2,则会多次输出。
public static void duplicateInt(int[] array) {
if (array == null) {
return;
}
// 存在重复数字,起码有两个以上的数字才可以
int len = array.length;
if (len < 2) {
return;
}
for (int i = 0; i < len; i++) {
// 数字中的数字范围在0到len-1之间,否则就认为是个错误的数组,不考虑重复数字问题
if (array[i] < 0 || array[i] > len - 1) {
return;
}
}
// 排序 Arrays.sort(array)
sort(array, 0, len - 1);
for (int i = 0; i < len - 1; i++) {
if (array[i] == array[i + 1]) {
System.out.print(array[i] + " ");
}
}
return;
}
/**
* 快排
* @param array
* @param lo
* @param hi
*/
public static void sort(int[] array,int lo ,int hi){
if(lo>=hi){
return ;
}
int index=partition(array,lo,hi);
sort(array,lo,index-1);
sort(array,index+1,hi);
}
public static int partition(int []array,int start,int end){
// 固定的切分方式
int key = array[start];
while(start < end){
// 从后半部分向前扫描
while(end > start && array[end] >= key){
end--;
}
array[start] = array[end];
// 从前半部分向后扫描
while(start < end && array[start] < key){
start++;
}
array[end]=array[start];
}
array[start]=key;
return end;
}
解法二
借助Map,因为只需遍历一次,利用Map来记录每个数字出现的次数,时间复杂度会降到0(n),但是会额外开辟O(n)的Map空间。好处是可以明确知道每个数字的重复次数。
public static void duplicateInt2(int[] array) {
if (array == null) {
return;
}
// 存在重复数字,起码有两个以上的数字才可以
int len = array.length;
if (len < 2) {
return;
}
for (int i = 0; i < len; i++) {
// 数字中的数字范围在0到len-1之间,否则就认为是个错误的数组,不考虑重复数字问题
if (array[i] < 0 || array[i] > len - 1) {
return;
}
}
Map<Integer, Integer> map = Maps.newHashMap();
for (int i = 0; i < len; i++) {
if (map.containsKey(array[i])) {
map.put(array[i], map.get(array[i]) + 1);
} else {
map.put(array[i], 1);
}
}
for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
if (entry.getValue() > 1) {
System.out.print(entry.getKey() + " ");
}
}
return;
}
解法三
题目中明确指出n个数字,其取值范围在0到n-1之间,所以可以认为
- 如果都不相等,则数组下标总能找到和其一致的数字
- 如果存在重复的,则某些数组下标应有一个以上的数字与之相一致
所以我们通过找与下标一致的数字,如果当前下标中放得数字与下标不一致,则看该数字对应的下标中的数字是否和当前下标中的数字是否一致
- 若一致,则输出,并找下一个重复数字
- 若不一致,则交换两个下标对应的数字,并重复此过程
这样,数字都落在与其一致的下标中,只有重复的数字,会落在其他与其不一致的(重复次数-1)下标中
public static void duplicateInt3(int[] array) {
if (array == null) {
return;
}
// 存在重复数字,起码有两个以上的数字才可以
int len = array.length;
if (len < 2) {
return;
}
for (int i = 0; i < len; i++) {
// 数字中的数字范围在0到len-1之间,否则就认为是个错误的数组,不考虑重复数字问题
if (array[i] < 0 || array[i] > len - 1) {
return;
}
}
// 比如 {2, 3, 1, 0, 2, 5, 3}
// array[0] = 2,不一致,array[2] = 1, 不相等,则交换,得到 {1, 3, 2, 0, 2, 5, 3}
// array[0] = 1,不一致,array[1] = 3, 不相等,则交换,得到 {3, 1, 2, 0, 2, 5, 3}
// array[0] = 3,不一致,array[3] = 0, 不相等,则交换,得到 {0, 1, 2, 3, 2, 5, 3}
// array[0] = 0,一致,现在下标0中存放了预期一致的数字,结束下标0的操作
// array[1] = 1,一致,现在下标1中存放了预期一致的数字,结束下标1的操作
// array[2] = 2,一致,现在下标2中存放了预期一致的数字,结束下标2的操作
// array[3] = 3,一致,现在下标3中存放了预期一致的数字,结束下标3的操作
// array[4] = 2,不一致,array[2] = 2,相等输出结果,无需交换,结束下标4的操作,此时 {0, 1, 2, 3, 2, 5, 3}
// array[5] = 5,一致,现在下标5中存放了预期一致的数字,结束下标5的操作
// array[6] = 3,不一致,array[3] = 3,相等输出结果,无需交换,结束下标4的操作,此时 {0, 1, 2, 3, 2, 5, 3}
for (int i = 0; i < len; i++) {
while (array[i] != i) {
if (array[i] == array[array[i]]) {
System.out.print(array[i] + " ");
break;
} else {
int temp = array[i];
array[i] = array[temp];
array[temp] = temp;
}
}
}
return;
}