注:此博客不再更新,所有最新文章将发表在个人独立博客limengting.site。分享技术,记录生活,欢迎大家关注
题目描述
在一个长度为n的数组里的所有数字都在0到n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
思路:
找数组中其中一个重复的数字/全部重复数字:①HashMap ②如果n个数字都在0~n - 1范围内,则可以将数组排成nums[i] == i 的形式 ③ 如果要求不能修改数组且空间复杂度O(1),可以对值的一定范围内的元素个数进行二分查找
第一次提交:排序,时间复杂度O(nlogn),空间复杂度O(1)
import java.util.Arrays;
public class Solution {
// Parameters:
// numbers: an array of integers
// length: the length of array numbers
// duplication: (Output) the duplicated number in the array number,length of duplication array is 1,so using duplication[0] = ? in implementation;
// Here duplication like pointor in C/C++, duplication[0] equal *duplication in C/C++
// 这里要特别注意~返回任意重复的一个,赋值duplication[0]
// Return value: true if the input is valid, and there are some duplications in the array number
// otherwise false
public boolean duplicate(int numbers[],int length,int [] duplication) {
Arrays.sort(numbers);
for (int i = 1; i < length; i ++) {
if (numbers[i] == numbers[i - 1]) {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}
结果:
答案错误:您提交的程序没有通过所有的测试用例
case通过率为83.33%
测试用例:
[]
对应输出应该为:
"false,-1"
你的输出为:
java.lang.NullPointerException
经验:应该先写测试用例,首先考虑特殊情况
import java.util.Arrays;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
// 添加numbers为空时的情况
if (length <= 0) {
duplication[0] = -1;
return false;
}
Arrays.sort(numbers);
for (int i = 1; i < length; i ++) {
if (numbers[i] == numbers[i - 1]) {
duplication[0] = numbers[i];
return true;
}
}
return false;
}
}
运行时间:27 ms
占用内存:8756K
第二次提交:用HashMap,时间复杂度O(1),空间复杂度O(n)
import java.util.HashMap;
import java.util.Map;
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if (length <= 0) {
duplication[0] = -1;
return false;
}
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < length; i ++) {
if (map.containsValue(numbers[i])) {
duplication[0] = numbers[i];
return true;
}
map.put(i, numbers[i]);
}
return false;
}
}
运行时间:14ms
占用内存:9556k
第三次提交:利用数组长度为n且所有数字都在1~n-1重排数组,使得num[i] == i,时间复杂度O(1),空间复杂度O(1)
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
if (length <= 0) {
duplication[0] = -1;
return false;
}
for (int i = 0; i < length; i++) {
int m = numbers[i]; // 0 <= numbers[i] <= n - 1
if (m != i) {
if (m != numbers[m]) {
numbers[i] = numbers[m];
numbers[m] = m;
} else {
duplication[0] = numbers[m]; // numbers[m] == numbers[i]
return true;
}
}
}
return false;
}
}
运行时间:15ms
占用内存:9540k
与我不同的排名前列答案:
public class Solution {
public boolean duplicate(int numbers[],int length,int [] duplication) {
StringBuffer sb = new StringBuffer();
for (int i = 0; i < length; i ++) {
sb.append(numbers[i] + "");
}
for (int j = 0; j < length; j ++) {
if (sb.indexOf(numbers[j] + "") != sb.lastIndexOf(numbers[j] + "")) {
duplication[0] = numbers[j];
return true;
}
}
return false;
}
}
运行时间:24ms
占用内存:8784k
升级版:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1到n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字。 例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
如果用上面第三次提交的方法,由于不能修改数组则需要另外开辟O(n)空间的数组,时间复杂度O(1),空间复杂度O(n)。
如果用二分查找只需要O(1)空间复杂度,时间复杂度为O(nlogn),因为函数countRange()将被调用O(logn)次,每次需要O(n)时间:
关于二分查找时间复杂度为O(logn)的解释:
二分查找的基本思想是将n个元素分成大致相等的两部分,去a[n/2]与x做比较,如果x=a[n/2],则找到x,算法中止;如果x<a[n/2],则只要在数组a的左半部分继续搜索x,如果x>a[n/2],则只要在数组a的右半部搜索x.
时间复杂度无非就是while循环的次数!
总共有n个元素,
渐渐跟下去剩余将要操作的元素数目就是n,n/2,n/4,....n/2^k,其中k就是循环的次数
由于你n/2^k取整后>=1
即令n/2^k=1
可得k=log2n,(是以2为底,n的对数)
所以时间复杂度可以表示O(logn)
public class Solution {
public static int duplicate(int numbers[],int length) {
if (length <= 0) {
return -1;
}
// 1 <= numbers[i] <= n
int start = 1;
int end = length - 1; // end = length - 1 = n + 1 - 1 = n
while (end >= start) {
int middle = start + (end - start) / 2;
int count = countRange(numbers,length,start,middle);
if (end == start) {
if (count > 1) return start;
else break;
} else if (count > middle - start + 1) {
end = middle;
} else {
start = middle + 1;
}
}
return -1;
}
public static int countRange(int[] numbers, int length, int start, int end) {
if (length <= 0) return 0;
int count = 0;
for (int i = 0; i < length; i ++)
if (numbers[i] >= start && numbers[i] <= end)
count++;
return count;
}
public static void main(String[] argv) {
int[] nums = {1,3,4,1};
int dup = duplicate(nums, nums.length);
System.out.println("duplicate is " + dup);
}
}
但是在某些特殊输入时二分搜索无法找出重复的数,如输入{1,3,4,1}则输出为-1。