题目:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
分析:本题可以使用一个新数组,长度n+1,全部元素初始化为0,把原数组的每个元素根据其值放到新数组中下标对应的位置,在赋值前可以先判断当前下标位置的元素值是否为0,如果为0就赋值,不为0说明这个就是重复的元素。但是空间复杂度为O(n) ,如果用一个HashMap来存,空间复杂度也是一样。
很显然,这并不会是面试官想要的答案,面试官这时候会说如果空间复杂度为O(1)呢?
《剑指Offer》一书中对该题的算法有一个场景没有考虑到,会导致没有查到重复元素的情况。在使用二分法缩小数组中重复元素在1~n的范围时,最终前后标可能会存在两种情况,一种是《剑指Offer》一书中的前后标记重合的情况,如{2,3,5,4,3,2,6},最后start和end都是3;一种是前后标记位相差1,如{2,3,5,4,3,2,6,7},最后start为3,end为4。
Java版本:
private static int getOneDuplicateNumber(int[] inputArray) {
if (inputArray == null || inputArray.length == 0 || inputArray.length == 1) {
return -1;
}
int length = inputArray.length;
int start = 1;
int end = length - 1;
while (end >= start) {
int middle = (start + end) / 2;
int countInRange = countRange(inputArray, length, start, middle);
//分两种情况 有可能最后两个指针重合,也有可能最后两个指针相差1
if (end == start) {
if (countInRange > 1) {
return start;
} else {
break;
}
} else if (end == start + 1) {
if (countInRange > 1) {
return start;
} else if (countRange(inputArray, length, end, end) > 1) {
return end;
} else {
break;
}
}
//二分查找,折半缩小范围
if (countInRange <= middle) {
start = middle + 1;
} else {
end = middle;
}
}
return -1;
}
private static int countRange(int[] inputArray, int length, int start, int end) {
if (inputArray == null) {
return 0;
}
int count = 0;
for (int i = 0; i < length; i++) {
if (inputArray[i] >= start && inputArray[i] <= end) {
count++;
}
}
return count;
}
Kotlin版本:
fun getOneDuplicateNumber(inputArray: IntArray?): Int {
if (inputArray?.isEmpty()!!) {
return 0
}
val length = inputArray.size
var start = 1
var end = length - 1
while (start <= end) {
val middle = (start + end) / 2
val count = countRange(inputArray, start, middle)
if (start == end) {
if (count > 1) {
return start
} else {
break
}
} else if (start + 1 == end) {
return if (count > 1) {
start
} else if (countRange(inputArray, end, end) > 1) {
end
} else {
break
}
}
if (count <= middle) {
start = middle + 1
} else {
end = middle
}
}
return -1
}
fun countRange(inputArray: IntArray?, start: Int, end: Int): Int {
if (inputArray?.isEmpty()!!) {
return 0
}
var count = 0
inputArray.forEach {
if (it in start..end) {
count++
}
}
return count
}
包含测试的完整代码:https://github.com/chenyucheng97/CodeingInterviews