面试题3:数组中重复的数字
题目:在一个长度为n的数组里的所有数字都在 0到 n-1的范围内。 数组中某些数字是重复的,但不知道有几个数字是重复的。也不知道每个数字重复几次。请找出数组中任意一个重复的数字。 例如,如果输入长度为 7的数组 {2,3,1,0,2,5,3},那么对应的输出是第一个重复的数字 2。
分析:
1、采用简单的方法,将输入的数组排序,从头扫描排序的数组,但是对一个长度为n的数组进行排序,时间复杂度为O(nlogn)。
2、利用哈希表存储数字,如果哈希表里还没有这个数字,就将其加入哈希表;有的话就找到一个重复的数字。这个算法的时间复杂度为O(n),但是空间复杂度为O(n)。
3、所有的数字都是在 0到 n-1的范围内,如果这个数组中没有重复的数字,那么当是数组排序之后数字 i 将出现在下标为 i的位置。由于数组中有重复的数
字,有些位置可能存在多个数字,同时有些位置可能没有数字。
从头到尾依次扫描这个数组中的每个数字。当扫描到下标为 i 的数字时,首先比较这个数字(用 m表示)是不是等于 i。如果是,接着扫描下一个数字。如果不是,再拿它和第 m个数字进行比较 。如果它和第 m个数字相等,就找到了一个重复的数字。如果它和第 m个数字不相等,就把第 i 个数字和第 m 个数字交换,把 m 放到属于它的位置。接下来再重复比较、交换的过程,直到发现重复的数字。
空间复杂度为O(1)。
代码实现
// 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 class Solution{
public boolean duplicate(int numbers[], int length,int[] duplication) {
if(numbers == null || length == 0 ) {
return false;
}
for(int i = 0; i < length - 1; i++) {
if(numbers[i] < 0 || numbers[i] > length - 1) {
return false;
}
while(numbers[i] != i) {
//如果第 i个数字 m 和第 m 个数字相等,则找到重复的数字
if(numbers[i] == numbers[numbers[i]]) {
duplication[0] = numbers[i];
return true;
}else {
int temp = numbers[i];
numbers[i] = numbers[numbers[i]];
numbers[numbers[i]] = temp;
}
}
}
return false;
}
}
扩展:在长度为n+1的数组里的所有数字都都在1~n的范围内,所以数组中至少有一个数字是重复的。请找出数组中任意一个重复的数字,但不能修改输入的数组。例如,如果输入长度为 8的数组 {2,3,5,4,3,2,6,7},那么对应的输出是重复的数字 2 或 3。
分析:如果不能修改数组中的数字,可以借助一个长度为n+1的数组,然后逐一把原数组的每个数字复制到辅助数组中下标为m的位置,但这需要O(n)的辅助空间。
将1 ~ n 的数字从中间的数字m分为两部分,前一半为1 ~ m,后面一半为m+1 ~ n。如果1 ~ m 的数字在整个数组中的个数超过m,那么这一半的区间里一定包含重复的数字;否则,另一半m+1 ~ n的区间里一定包含重复的数字,我们可以继续把包含重复数字的区间一分为二,知道找到一个重复的数字,类似于二分查找。
代码实现
public class Solution{
public static int getDuplication(int[] numbers, int length) {
if(numbers == null || length == 0) {
return -1;
}
int start = 1; //1
int end = length - 1; //n
while(start <= end) {
int mid = (end - start)/2 + start; //1~n的中间数
//计算1~mid的数字在这个数组中出现的次数
int count = countRange(numbers, length, start, mid);
if(end == start) {
if(count > 1) return start;
else break;
}
if(count > (mid - start + 1)) end = mid; //重复数字在前一半
else start = mid + 1; //重复数字在后一半
}
return -1;
}
public static int countRange(int[] numbers, int length, int start, int end) {
if(numbers == null) return 0;
int count = 0;
for(int i = 0; i < length - 1; i++) {
if(numbers[i] >= start && numbers[i] <= end) {
count++;
}
}
return count;
}
}
但是该方法不能保证找出所有的重复的数字,比如,不能找出 {2,3,5,4,3,2,6,7}中的重复数字2。
面试题4:二维数组中的查找
题目:在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序。请完成一个函数,输入这样的一个二维数组和一个整数,判断数组中是否含有该整数。
分析:首先选取数值中右上角的数字。如果该数字等于要查找的数字,则查找过程结束;如果该数字大于要查找的数字,则剔除这个数字所在的列;如果该数字小于要查找的数字,则剔除这个数字所在的行。
代码实现
public class Solution{
public boolean Find(int target, int[][] array) {
boolean flag = false;
int rows = array.length;
int columns = arrat[0].length;
int row = 0;
int column = columns - 1;
while(row < rows && column >= 0) {
if(array[row][column] == target) {
flag = true;
break;
}else if(array[row][colunm] > target) {
column--; //去除列
}else {
row++; //去除行
}
}
return flag;
}
}