找出數組中重複的數字:在一個長度為n的數組裡的所有數字都在0~n-1的範圍内。數組中某些數字是重複的,但不知道有幾個數字重複了,也不知道每個數字重複了幾次。請找出數組中任意一個重複的數字。例如,如果輸入長度為7的數組{2,3,1,0,2,5,3},那麼對應的輸出是重複的數字2或者3。
審題:長度為n的數組,0~n-1的範圍,确定了肯定至少一個重複。注意是找出任意一個重複的數字即可。
思路:如果要排個序起手的話,就先要O(nlogn)的代價,然後再找。但是題目隻需要找到1個重複,那麼排序是有相當大的部分是浪費的。如果用哈希表呢?從頭掃到尾對這個數組,然後添加到表中,那麼第一個重複的值就很容易找到,似乎也沒有其他特别大的浪費空間。那麼代價就是O(n),感覺提高了不少,但是還要一個大小為O(n)的哈希表為代價,總體來說劃得來。既然要另外空間,那麼有沒有不用輔助空間的呢?呀,好像給我的數組不就是一個空間嗎?原址搞起不是棒棒的?再看看題目,長度n,0~n-1範圍,那麼下标等于值?有搞頭!
做法:現在讓我們重排這個數組。從頭到尾一次掃描這個數組中的每個數字。當掃描到下标為i的數字時,首先比較這個數字(用m
表示)是不是等于i。如果是,則掃描下一個數字;如果不是,則再拿它和第m個數字進行比較。如果它和第m個數字相等,就找到
了一個重複的數字(該數字在下标為i和m的位置都出現了);如果它和第m個數字不相等,就把第i個數字和第m個數字交換,把m
放到屬于它的位置。接下來再重複這個比較、交換的過程,直到我們發現一個重複的數字。
代碼:
class Program
{
static void Main(string[] args)
{
int[] sample = { 5, 4, 2, 1, 3, 3 };
int result = -1;
Console.WriteLine(Duplicate(sample,sample.Length,ref result));
Console.WriteLine(result);
Console.ReadKey();
}
static bool Duplicate(int[] numbers, int length,ref int result)
{
if (numbers == null || length <= 0)
return false;
for (int i = 0; i < length; i++)
{
if (numbers[i] < 0 || numbers[i] > length - 1)
return false;
}
for (int i = 0; i < length; i++)
{
while (numbers[i] != i)
{
if (numbers[i] == numbers[numbers[i]])
{
result = numbers[i];
return true;
}
int temp = numbers[i];
numbers[i] = numbers[temp];
numbers[temp] = temp;
}
}
return false;
}
}
總結:注意到代碼中有個雙重循環,那麼是不是就以為這該算法起碼是O(n^2)呢?其實不然,因為每個數字最多隻要交換兩次
就能找到i,m相等的位置,是以總的複雜度還是O(n),但原址交換,不需要輔助空間。
心得:這些問題其實就是寫那麼一個方法(不行就兩個)解決,傳進來的參數也是顯而易見的,重要的還是邏輯,細節就在于
把控傳入參數,魯棒性的檢測是必不可少的,是面試的加分項,邏輯通順還要缜密。開局八成就要來幾個if來檢驗下原始輸入
是否可行。然後才是邏輯核心。