题目:在一个长度为n的数组里的所有数字都在0~n-1的范围内,找出对应重复的数字。例如输入长度为7的数组int[] m ={2,3,1,0,2,5,3},则重复的数字为2和3。
解法一:使用快排,排序后再去做比较找重复的数字,这个方法的时间复杂度为O(nlogn),这是最常见的方法,但是不是最快的办法。
解法二:这里有个重点,就是n属于[0, n-1],也就是说如果没有重复的数字,那么下标i和m[i]是相等的,这个很好理解吧。那么让我们重排数组,从头到尾扫描这个数组,过程如下:
- 1.当扫描到下标i的数字m[i],把i和m[i]作比较,如果相等,则扫描下一个,否则进入第二步。
- 2.将m[i]和第m[i]个数字进行比较,如果相等,这就是一个重复的数字,扫描下一个数字。否则进入第三步。
- 3.将m[i]和第m[i]进行交换,继续仅从头开始新的一轮扫描,直到结束。
上面的过程实质上就是比较交换的过程。
这里给出java版本的代码,如下:
public class Main {
static int[] a = {2, 3, 1, 0, 2, 5, 3};
public static void main(String[] args){
duplicate(a);
}
private static void duplicate(int[] numbers) {
if (numbers == null || numbers.length == 0) return;
for (int i = 0; i < numbers.length; i++){
while (numbers[i] != i){//第一步
if (numbers[i] == numbers[numbers[i]]){//第二步
System.out.println("重复的数字-----> " + numbers[i]);
break;
}
//第三步
int tmp = numbers[i];
numbers[i] = numbers[tmp];
numbers[tmp] = tmp;
}
}
}
}
代码中尽管有两重循环,但是最多进行了2次交换就找到了它的位置,因此总的时间复杂度为O(n),所有的操作都在同一个数组上,没有额外的分配内存空间,空间复杂度为O(1);