面試題3:數組中重複的數字
題目一:找出數組中重複的數字。
在一個長度爲n的數組裏的所有數字都在0~n-1的範圍內。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。例如:如果輸入長度爲7的數組{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者3。
解答:方法1:先對輸入的數組排序,然後從頭開始掃描數組,找到重複數字。時間複雜度:O(nlogn)。代碼如下:
int Partition(int *numbers,int left,int right)
{
int tmp = numbers[left];
while(left < right)
{
while(left < right && numbers[right] > tmp)
{
right--;
}
if(left < right)
{
numbers[left++] = numbers[right];
}
else
{
break;
}
while(left < right && numbers[left] < tmp)
{
left++;
}
if(left < right)
{
numbers[right--] = numbers[left];
}
else
{
break;
}
}
numbers[left] = tmp;
return left;
}
//快速排序
void quickSort(int *numbers,int left,int right)
{
if(NULL == numbers || left == right)
{
return;
}
int sit = Partition(numbers,left,right);
if(left + 1 < sit)
{
quickSort(numbers,left,sit-1);
}
if(sit + 1 < right)
{
quickSort(numbers,sit+1,right);
}
}
//是否存在重複數字
bool duplicate(int numbers[], int length, int* duplication)
{
if(NULL == numbers || NULL == duplication || length <= 0)
{
return false;
}
int i = 0;
for(;i < length;i++)
{
if(numbers[i] < 0 || numbers[i] > (length - 1))
{
return false;
}
}
quickSort(numbers,0,length-1);
for(i = 0;i < (length - 1);i++)
{
if(numbers[i] == numbers[i+1])
{
*duplication = numbers[i];
return true;
}
}
return false;
}
方法2:建立哈希表。從頭到尾掃描數組,分別判斷每一個數字是否在哈希表中,如果不在就加入哈希表,如果在就找到了一個重複數字。時間複雜度:O(n),空間複雜度:O(n)。代碼如下:
bool duplicate(int numbers[], int length, int* duplication)
{
if(NULL == numbers || length <= 0)
{
return false;
}
bool *tmpArr = (bool *)malloc(length);
if(NULL == tmpArr)
{
return false;
}
int i = 0;
for(;i < length;i++)
{
if(numbers[i] < 0 || numbers[i] > (length - 1))
{
return false;
}
}
for(i = 0;i < length;i++)
{
tmpArr[i] = 0;
}
for(i = 0;i < length;i++)
{
if(tmpArr[numbers[i]])
{
*duplication = numbers[i];
return true;
}
tmpArr[numbers[i]] = 1;
}
return false;
}
方法3:重排數組。從頭到尾掃描數組,當掃描到下標爲i的數字時,比較這個數字m是否等於i。如果是則繼續掃描下一個,如果不是就拿它和第m個數字比較,如果相等就找到了一個重複的數字,如果不等則交換。直到發現一個重複的數字。
bool duplicate(int numbers[], int length, int* duplication)
{
if(NULL == numbers || length <= 0)
{
return false;
}
int i = 0;
for(;i < length;i++)
{
if(numbers[i] < 0 || numbers[i] > (length - 1))
{
return false;
}
}
for(i = 0;i < length;)
{
if(i == numbers[i])
{
i++;
}
else if(numbers[i] == numbers[numbers[i]])
{
*duplication = numbers[i];
return true;
}
else
{
int tmp = numbers[i];
numbers[i] = numbers[tmp];
numbers[tmp] = tmp;
}
}
return false;
}
題目二:不修改數組找出重複的數字。
在一個長度爲n+1的數組裏的所有數字都在1~n的範圍內,所以數組中至少有一個數字是重複的。請找出數組中任意一個重複的數字,但不能修改輸入的數組。例如,如果輸入長度爲8的數組{2,3,5,4,3,2,6,7},那麼對應的輸出是重複的數字2或者3。
解答:可以把從1~n的數字從中間的數字m分成兩部分,前面一半爲1~m,後面一半爲m+1~n。如果1~m的數字的數目超過m,那麼這一半的區間裏一定包含重複的數字;否則,另一半區間一定包含重複的數字。繼續把包含重複數字的區間一分爲二,直到找到一個重複的數字。
以長度爲8的數組{2,3,5,4,3,2,6,7}爲例分析查找的過程。根據題目要求,這個長度爲8的所有數組都在1~7的範圍內。中間的數字4把1~7分爲兩段,一段是1~4,另一段是5~7。接下來統計1~4這四個數字在數組中出現的次數,他們一共出現了5次,因此這4個數字中一定有重複的數字。接下來再把1~4的範圍一分爲二,一段是1、2兩個數字,另一段是3、4兩個數字。數字1和2一共出現了2次,數字3和4一共出現了3次,這意味着3、4中一定有一個數字重複了。再分別統計這兩個數字出現的次數,發現數字3出現了兩次。
int countRange(const int *numbers,int length,int left,int right)
{
if(NULL == numbers || length <= 0 || left > right || left < 1 || right > length-1)
{
return -1;
}
int count = 0;
for(int i = 0;i < length;i++)
{
if(numbers[i] >= left || numbers[i] <= right)
{
count++;
}
}
return count;
}
int getDuplication(const int* numbers, int length)
{
if(NULL == numbers || length <= 0)
{
return -1;
}
int i = 0;
for(;i < length;i++)
{
if(numbers[i] < 1 || numbers[i] > length-1)
{
return -1;
}
}
int left = 1;
int right = length - 1;
while(left <= right)
{
int mid = ((right - left) >> 1) + left;
int count = countRange(numbers,length,left,mid);
if(left == right)
{
if(count > 1)
{
return left;
}
else
{
break;
}
}
if(count > (mid-left+1))
{
right = mid;
}
else
{
left = mid + 1;
}
}
return -1;
}
上述代碼按照二分查找的思路,如果輸入長度爲n的數組,那麼函數countRange將被調用O(logn)次,每次需要O(n)的時間,因此總的時間複雜度爲O(nlogn),空間複雜度爲O(1)。需要指出的是,這種算法不能找出所有重複的數字。
面試題4:二位數組中的查找
題目:在一個二維數組中,每一行都按照從左到右遞增的順序,每一列都按照從上到下遞增的順序排序。請完成一個函數,輸入
這樣的一個二維數組和一個整數,判斷數組中是否含有該整數。
解答:首先選取數組中右上角的數字,如果該數字等於要查找的數字,則查找過程結束;如果該數字大於要查找的數字,則剔除
這個數字所在的列;如果該數字小於要查找的數字,則剔除這個數字所在的行。也就是說,如果要查找的數字不在數組的右上
角,則每一次都在數組的查找範圍中剔除一行或者一列,這樣每一步都可以縮小查找範圍,直到找到要查找的數字。代碼如下:
bool Find(int target, vector<vector<int> > array)
{
bool res = false;
if(array.size() == 0)
{
return res;
}
int row = array.size() - 1;
int col = array[0].size() - 1;
int i = 0;
int j = col;
while(i <= row && j >= 0)
{
if(array[i][j] == target)
{
res = true;
break;
}
else if(array[i][j] > target)
{
j--;
}
else
{
i++;
}
}
return res;
}