1. 程式人生 > >資料結構演算法題/尋找陣列中唯一出現兩次的數

資料結構演算法題/尋找陣列中唯一出現兩次的數

假設你有一個用1001個整陣列成的陣列,這些整數是任意排列的,但是你知道所有的整數都在1到1000(包括1000)之間。此外,除一個數字出現兩次外,其他所有數字只出現一次。假設你只能對這個陣列做一次處理,用一種演算法找出重複的那個數字。如果你在運算中使用了輔助的儲存方式,那麼你能找到不用這種方式的演算法嗎?

這道題目在四月份騰訊實習生的二面時候被問到,當時壓根就沒有時間準備演算法的東西,結果在那裡胡說八道了一大堆,結果可想而知,現在把解法給記錄下來。

第一種方法:
如果允許使用額外的空間,那就比較好吧,這些數,只有一個出現了兩次,其他都只出現一次,那麼,我們就用個數組來統計一下每個數出現的次數,在統計過程中,如果出現一個是出現2次的話,那麼毫無疑問,程式可以結束了,返回相應的數字即可。由於我們只需要遍歷陣列一次,所以時間複雜度是O(n)

優點:效率高
缺點:消耗的記憶體空間過大

關於空間的,本人在這裡有個疑問,如果有人能為我解惑,那麼將感激不敬。
空間複雜度到底是O(1)還是O(n)?我們定義的陣列是n個元素,所以我理所當然地覺得空間複雜度是O(n),但是曾在一些書籍上面看到,由於n是確定的,所以相當於是恆量空間,所以空間複雜度是O(1),到底是哪個我也不清楚,希望有人能為我解惑。

int FindRepeat(int a[])
{
 int count[1000]={0};
 for(int i=0;i<=1000;i++)
 {
  if(1==count[a[i]])
  {
   return a[i];
  }
  else
  {
   count[a[i]]++;
  }  
 }
}

根據這種使用額外陣列做標記的方法,並且考慮到元素的特殊性,只有一個是出現了兩次,那麼我們其實可以不用統計次數,而是簡簡單單地做下標記即可,比如說,我們可以定義一個bool型的陣列,全部初始化為false,進行遍歷的時候,相應的元素如果出現就將其設定為true,當一個標記是true,卻再次掃描到該元素的時候,那麼則該元素出現了兩次了,一個bool型佔用的記憶體空間是一個位元組,而int型別佔用的記憶體是4個位元組,這樣子,佔用的空間已經大大地減少了。那麼是否還有類似的辦法,但是佔用的記憶體空間更少的呢?true和false僅僅只是兩個值,考慮到計算機的位,無非也就0,1兩種值,所以我們可以使用位來進行標記,於是便要使用到了點陣圖資料結構這種東西了(關於點陣圖資料結構,我前面有一篇文章,可以參考參考)1000位,8位就是一個位元組,1000位等於125位元組,所以可用32個int型別來表示,這樣子記憶體空間已經大大減少了。


第二種方法:
雖然第一種解法到最後我們能大大地減少記憶體空間,不過還是佔用了一定的記憶體空間,面試中面試官估計還是不會這麼善罷甘休的,肯定希望我們再減少空間的使用。第二種方法主要用到的是數學的知識,1001個數,有一個出現了兩次,那麼我把這1001個數加起來,用和減去1到1000的和,很明顯,差就是出現了兩次的那個數了。

int FindRepeat(int a[])
{
 int sum1=0,sum2=0;
 for(int i=0;i<=1000;i++)
 {
  sum1+=a[i];
 }

 for(int j=1;j<=1000;j++)
 {
  sum2+=j;
 }

 return sum1-sum2;
}

這種方法使用了兩次for迴圈,很明顯時間複雜度是O(n+n),也就是O(n),這樣子就可以在很少使用記憶體空間的情況下,而且線上性時間內找出重複的數了。當然,這個演算法還可以優化下,考慮1到1000的和,由於1到1000是一個等差數列,所以我們可以用公式計算sum2=1001*1000/2計算1到1000的和,這樣子就去掉了第二個迴圈了,當然,時間複雜度還是沒有改變,依然是O(n)

優點:記憶體空間消耗很少,
缺點:由於這種方法要完全遍歷陣列,所以效率要低於第一種方法,除非很不巧,重複是出現在最末尾。


第三種方法:
這種方法主要使用到了位運算,其實位運算有很多有用的性質,只是為人們所不熟悉罷了。

這裡主要用到的是異或運算的性質,a^a^b=b,以及異或運算滿足結合律和交換律

將1001個數進行異或,結果與1到1000異或的結果進行異或,得到的值即為所求。

看個簡單的例子吧
1,2,3,4,4,5
1,2,3,4,5

看出玄機了吧,其實就是相當於我們手動為第一個陣列構造一個副本,以便發揮a^a^b=b的性質。

int a[7]={1,4,5,6,2,3,5};
    int i=0,j=0;
    int b[6]={1,4,6,2,3,5};
    int temp=a[0];
    for(i=1;i<7;++i){
        temp=temp^a[i];
    }
    int temp2=b[0];
    for(j=1;j<6;++j){
        temp2=temp2^a[j];
    }
    int result = temp^temp2;
    cout<<"result:"<<result;

https://blog.csdn.net/sole_cc/article/details/45887481

https://blog.csdn.net/runner668/article/details/79941667