1. 程式人生 > >找出陣列中唯一的重複元素

找出陣列中唯一的重複元素

題目:陣列 arr[N],1 至 N 這 N - 1 個數存放在 arr[N] 中,其中某個數重複一次,寫一個函式,找出重複的數字。要求每個陣列元素只能訪問一次,不用輔助儲存空間。
分析:由於題目要求每個陣列元素只能訪問一次,不用輔助儲存空間,可以從原理上入手,採用數學求和法,因為只有一個數字重複一次,而數又是連續的,根據累加和原理,對陣列的所有項求和,然後減去 1 至 N - 1 的和,即為所求的重複數。

#include <iostream>
#define SUM(x) (x*(x+1)/2)

int findData(int arr[], int N)
{
    int
sum = 0; for (int i = 0; i < N; i++) sum += arr[i]; int sum2 = SUM((N-1)); // 此處的N-1,一定要用小括號括起來。 return (sum - sum2); } int main(int argc, const char * argv[]) { int arr[] = {1,2,5,6,2,3,4,7}; int len = sizeof(arr)/sizeof(int); printf("唯一重複的元素是 %d\n", findData(arr, len)); return
0; }

拓展:如果題目沒有要求每個陣列元素只能訪問一次,並且也可以使用輔助空間,還可以使用以下方法來求解。
(1)異或法
根據異或法的計算方式,每兩個相異的數執行異或運算之後,結果為1;每兩個相同的數異或之後,結果為0,所以陣列 arr[N] 中的 N 個數異或結果 與 1 至 N - 1 異或的結果再做異或,得到的值即為所求。
設重複數為 A,其餘 N - 2 個數異或結果為 B,N 個數異或結果為 A^A^B,1 至 N - 1 異或結果為 A^B,由於異或滿足交換律和結合律,且 X^X = 0,0^X = X,則有 (A^B)^(A^A^B) = A^B^B = A。

(2)Hash法
<1> 首先,陣列元素的取值範圍是 1 ~ N - 1,其中最大的數值為 N - 1,所以要申請一個長度為 N 的新陣列(即按照集合中最大元素max建立一個長度為max+1的新陣列),並將新陣列元素全部初始化為0。
<2> 然後從頭開始遍歷原陣列arr[N],取每個陣列元素 arr[ i ] 的值,將新陣列中“以該值作為下標的元素“置1,如果已經置過1了,那麼該數就是重複的數。空間複雜度為O(N)。

備註:Hash法 和 點陣圖法(bitmap) 的比較
<1> 上述這種給新陣列“初始化時置零、其後置一“的做法,類似於點陣圖的處理方法,所以有人也稱上述方法為--點陣圖法。
<2> 所謂bitmap就是用一個bit位來標記某個元素對應的value,而key即是這個元素。由於採用bit為單位來儲存資料,因此在可以大大的節省儲存空間。
<3> 點陣圖法,由於每個數都是互斥的,所以它本身就是一個最好的雜湊(即使用的也是hash對映),所以點陣圖法也可算作hash演算法。
<4> 點陣圖通過使用 位陣列 來表示某些元素是否存在,位陣列中的元素取值非0即1,沒有其他可能,而且真正使用點陣圖法時,一定會涉及到位運算,如’ | ’ 、’ & ‘和‘<< ’ 等位運算。
<5> 如果只是使用了位運算中“非0即1“的思想來判重,而沒有使用“位陣列 + 位運算“,那麼我認為稱呼這類方法為hash法更合適一些,不應該稱為點陣圖法。
<6> 因為需要用到位運算,所以點陣圖法一般只適用於“非重複的正整數的排序“、查詢、判重、刪除。