剑指OFFER笔记_03_2_不修改数组找出重复的数字_JAVA实现
题目:不修改数组找出重复的数字
- 在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
题设条件中值得关注的点
- 数组长度为n+1,数字范围为1~n,也就是说n+1长度的数组中有n“种”数,必然有一个数是重复出现的,我们需要做的是找到它。
解题思路
- 第一种方法时间复杂度为O(n),空间复杂度为O(n),用空间换时间。
- 这种方法与题目01类似,创建一个与原数组长度相同为n+1的数组作为辅助,将原数组中的每个元素的作为下标存入辅助数组中,此时必然出现这种情况:试图存入一个数,但发现这个位置上已经有一个数了,此时该值就是重复的数字,返回该值。
- 第二种方法时间复杂度为O(nlogn),空间复杂度为O(1),用时间换空间。
- 这种方法的思想类似于二分查找。
- 假如n=7,数字范围为1~7,一共有8个数。start=1,end=7,middle=(1+7)/2=4。此时将范围缩小至1~4(二分)。
- 对1~4范围内的数进行计数,存于count,若出现了5次或以上,说明此区间存在重复的数(tips:但是不一定只存在于此区间),end=middle,开始新一轮检索。
- 否则start=middle+1,更改区间进行检索。
- 当出现start=middle的情况时,若此时计数结果count>1,则说明只有一个数的区间,出现了不止一次该数,显然该数重复出现,返回该数。若count<=1,返回0(无重复出现,若用例满足题设条件,不会出现该情况,但出于健壮性考虑,应当加上这一部分)。
- 此种方法有一个缺陷。比如在题设给的用例中,检测范围到了1~2的时候,count=2,理论上是没有重复的,但是实际上出现了两个2,没有1,不过由于缺少了1出现,所以除了2重复外,还有其他的重复元素,代码至少可以找到其他的重复的数字。此缺陷在此题要求中(只需要找到一个重复元素)可以接受,若更换条件要求,需要重新考虑更好的方法。
代码部分
第一种方法
函数主体部分
package q02;
public class Question02 {
public static int getDuplication(int[] numbers, int length)
{
if(numbers == null || length <= 0)
{
return -1;
}
for (int i = 0; i < numbers.length; i++)
{
if(numbers[i] <= 0 || numbers[i] >= length)
{
return -1;
}
}
int[] buffer = new int[length];
for (int i = 0; i < numbers.length; i++)
{
int temp=numbers[i];
if(buffer[temp] != temp)
{
buffer[temp] = temp;
}else
{
return temp;
}
}
return 0;
}
}
测试部分
package q02;
public class TestApp02 {
public static void main(String[] args) {
int[] numbers1 = {2,4,5,4,3,2,6,7};
int result1 = Question02.getDuplication(numbers1, numbers1.length);
System.out.println("result1: " + result1);
int[] numbers2 = null;
int result2 = Question02.getDuplication(numbers2, 0);
System.out.println("result2: " + result2);
int[] numbers3 = {5,1,2,3,5,4};
int result3 = Question02.getDuplication(numbers3, numbers3.length);
System.out.println("result3: " + result3);
}
}
测试运行结果
第二种方法
函数主体部分
package q02;
public class Question2 {
public static int getDuplication(int[] numbers, int length)
{
if(numbers == null || length <= 0)
{
return -1;
}
int start = 1;
int end = length-1;
while(end >= start)
{
int middle = (start + end) / 2;
int count = countRange(numbers, length, start, middle);
if(start == end)
{
if(count > 1)
{
return start;
}else
{
break;
}
}
if(count > middle - start + 1)
{
end = middle;
}else
{
start = middle+1;
}
}
return 0;
}
public static int countRange(int[] numbers, int length, int start, int middle)
{
if(numbers == null || length <= 0)
{
return 0;
}
int count = 0;
for(int i = 0; i < length; i++)
{
if(numbers[i] <= middle && numbers[i] >= start)
{
count++;
}
}
return count;
}
}
测试部分
package q02;
public class TestApp {
public static void main(String[] args) {
int[] numbers1 = {2,4,5,4,3,2,6,7};
int result1 = Question2.getDuplication(numbers1, numbers1.length);
System.out.println("result1: " + result1);
int[] numbers2 = null;
int result2 = Question2.getDuplication(numbers2, 0);
System.out.println("result2: " + result2);
int[] numbers3 = {5,1,2,3,5,4};
int result3 = Question2.getDuplication(numbers3, numbers3.length);
System.out.println("result3: " + result3);
}
}
测试运行结果