面试题3:数组中重复的数字
考点:1. 一维数组 2. 二分查找
题目一: 找出数组中重复的数字
在一个长度为n的数组里的所有数字都在0到n-1的范围内。数组中某些数字是重复的,但不知道有几个数字重复了,也不知道每个数字重复的次数。请找出数组中任意一个重复的数字。例如如果输入长度为7的数组{2,3,1,0,2,5,3},那么对应的输出是重复的数字2或者3。
1. 先对数组进行排序,再从头到尾扫描数组。排序时间复杂度为O(nlogn)
2. 用一个数组来构造简单的哈希表,空间复杂度为O(n)
3. 从头到尾依次扫描数组中的每个数字。当扫描到下标为i的数字m时,如果m=i,则继续扫描下一个数字;如果m!=i,则将m与第m个数字进行比较,如果不同,则将m放到第m个位置上;若m等于第m个数字,则m为重复数字。时间复杂度O(n),空间复杂度O(1)
// 方法一:快排+遍历
// 注意当没有重复数字时的输出
class Q3 {
public static void main(String[] args) {
// 不含重复数字
int[] intList = new int[] {4,3,3,1,5,2,6,7,9,8};
//数组排序
Qsort(intList, 0, intList.length-1);
showList(intList);
//遍历输出重复数字
int flag = 0;
for(int i =0;i<intList.length-1;i++) {
if (intList[i]==intList[i+1]){
flag = 1;
System.out.println(intList[i]);
}
}
if(flag==0) {
System.out.println(-1);
}
}
public static void Qsort(int[] L,int left,int right) {
int loc;
if(left<right) {
loc = moveElement(L, left, right);
// loc位置为正确位置,不用再挪动
Qsort(L, left, loc-1);
Qsort(L, loc+1, right);
}
}
public static int moveElement(int[] L,int left,int right) {
int low = left;
while(left<right) {
while(L[right]>=L[low] && left<right) {
right--;
}
while(L[left]<=L[low] && left<right) {
left++;
}
if(L[left]>L[low] && L[right]<L[low]) {
swap(L, left, right);
}
}
swap(L, low, left);
return left;
}
public static void showList(int[] L) {
for (int i = 0; i < L.length; i++) {
System.out.print(String.valueOf(L[i]) + ' ');
}
System.out.println();
}
public static void swap(int[] L,int i,int j) {
int temp = L[i];
L[i] = L[j];
L[j] = temp;
}
}
//方法二
//注意:
//1.检查是否输入的数组包含0~n-1之外的数字(不检查会数组超界)
//2.没有重复数字时的输出
class Q3 {
public static void main(String[] args) {
// 不含重复数字
int[] intList = new int[] {1,2,3,4,5,6,7,8};
int solution = Solution(intList);
System.out.println(solution);
// 无效的测试用例:长度为n的数组中包含0~n-1之外的数字
intList = new int[] {1,10,11};
solution = Solution(intList);
System.out.println(solution);
// 正常测试用例
intList = new int[] {1,2,4,4,4,5,6};
solution = Solution(intList);
System.out.println(solution);
}
public static int Solution(int[] intList) {
// 识别空
if (intList.length == 0) {
return -1;
}
//用数组来实现简单的哈希
int[] hashList = new int[intList.length];
for(int i=0;i<intList.length;i++) {
// 检查是否输入的数组包含0~n-1之外的数字,包含则跳出
if(intList[i]>hashList.length-1) {
return -1;
}
if(hashList[intList[i]]==0) {
hashList[intList[i]]++;
}else {
return intList[i];
}
}
return -1;
}
}
// 方法三
// 注意:是否有超界数字
class Q3 {
public static void main(String[] args) {
// 不含重复数字
int[] intList = new int[] {4,11,3,1,5,2,6,7,9,11};
int num = findN(intList);
System.out.println(num);
}
public static int findN(int[] L) {
for(int i=0;i<L.length;i++) {
if(L[i]!=i) {
while(L[i]!=i) {
if(L[i]!=L[L[i]]) {
swap(L, i, L[i]);
}else {
return L[i];
}
if(L[i]>L.length-1) {
return -2; //包含0~n-1以外的数字
}
}
}
}
return -1; //无重复数字
}
public static void swap(int[] L,int i,int j) {
int temp = L[i];
L[i] = L[j];
L[j] = temp;
}
}
题目二:不修改数组找出重复的数字
在一个长度为n+1的数组里的所有数字都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但是不能修改输入的数组。例如,如果输入长度为8的数组{2,3,5,4,3,2,6,7},那么对应的输出是重复的数字2或者3。
1. 构造长度为n+1的辅助数组,对原数组进行逐一检查
2. 按二分查找的方法,在原数组上进行查找重复数字:该算法不能找出所有的重复数字——以时间换空间
// 方法一
class Q3 {
public static void main(String[] args) {
// 不含重复数字
int[] intList = new int[] {4,8,3,0,5,2,6,7,9,1};
int fount = findN(intList);
System.out.println(fount);
}
public static int findN(int[] L) {
int len = L.length;
if(len==0) {
return 0;
}
int[] temp = new int[len];
for(int i=0;i<len;i++) {
// 防止数组中有超过n+1的数
if(L[i]>len) {
return -2;
}
if(temp[L[i]]==0) {
temp[L[i]]++;
}else {
return L[i];
}
}
// 数组中没有重复数字
return -1;
}
}
// 方法二
class Q3 {
public static void main(String[] args) {
// 不含重复数字
int[] intList = new int[] {1,4,4,6,5,3,0};
int Result = findN(intList);
System.out.println(Result);
}
public static int findN(int[] L) {
// 检查为空数组
if(L.length==0) {return -1;}
// 检查小于1或超过n的数
for (int i = 0; i < L.length; i++) {
if (L[i] < 1 || L[i] >= L.length) {
return -1;
}
int start = 0;
int end = L.length-1;
int count = 0;
while(start<=end) {
if(start==end) {
count = countN(L, start, end);
if(count>1) {
return start;
}else {
break;
}
}
int mid = (start+end)/2;
count = countN(L, start, mid);
// 如果大于本该有的数字个数 则继续在该范围内搜索
if(count > mid-start+1) {
end = mid;
}else {
start = mid+1;
}
}
}
return -1;
}
public static int countN(int[] L,int start,int end) {
int count=0;
for(int i =0;i<L.length;i++) {
if(L[i]>=start && L[i]<=end){
count++;
}
}
return count;
}
}