参考自:《剑指Offer——名企面试官精讲典型编程题》
题目:找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了, 也不知道每个数字重复了几次。请找出数组中任意一个重复的数字。例如,如果输入长度为7的数组{2, 3, 1, 0, 2, 5, 3},那么对应的输出是重复的数字2或者3。
主要思路: 数组中的数字范围为0~n-1,若没有重复数字,则排序后数字i出现在数组下标为i的位置。但是,因为有重复数字,所以数字和其下标不是一一对应的关系。重排该数组(numbers),从头到尾扫描,当扫描到下标为i时,判断其数值(m)是否等于i。
- 若相等,则继续扫描下一位置。
- 若不相等,则将该数值m与下标为m的数值(numbers[m])比较:
2.1 若这两个数相等,则找到了第一个重复的数字(该数字在下标为i和m的位置都出现了);
2.2 若不相等,则交换这两个数的位置,使得数值m 与其下标相等。接下来继续比较交换,直到找到重复的数字。
关键点: 找到数值与其索引的关系。
时间复杂度:O(n)
public class DuplicateNumber {
public static void main(String[] args) {
int[] numbers = {2, 3, 1, 0, 2, 5, 3};
int[] duplications = new int[1];
boolean isDuplicated = duplicate(numbers, numbers.length, duplications);
if (isDuplicated) {
// 2
System.out.println(duplications[0]);
}
}
private static boolean duplicate(int[] numbers, int length, int[] duplication) {
if (numbers == null || length <= 0) {
return false;
}
//判断输入数组是否正确
for (int i = 0; i < length; i++) {
if (numbers[i] < 0 || numbers[i] > length - 1) {
return false;
}
}
for (int i = 0; i < length; i++) {
//循环直到数值和下标相等
while (numbers[i] != i) {
int currentNumber = numbers[i];
//该数字出现两次
if (currentNumber == numbers[currentNumber]) {
duplication[0] = currentNumber;
return true;
}
//交换到正确位置
int temp = numbers[currentNumber];
numbers[currentNumber] = currentNumber;
numbers[i] = temp;
}
}
return false;
}
}