1-1000放在含有1001個元素的數組中,只有唯一的一個元素值重複,其它均只出現一次.每個數組元素只能訪問一次,設計一個算法,將它找出來;不用輔助存儲空間。
(1) 方法一:(當N爲比較大時警惕溢出)
將1001個元素相加減去1,2,3,……1000數列的和,得到的差即爲重複的元素。
int Find(int* a)
{
int i;
for (i = 0;i<=1000;i++)
a[1000] += a[i];
a[1000] -= (i*(i-1))/2; //i的值爲1001
return a[1000];
}
(2) 方法二:
數組取值操作可以看做一個特殊的函數f:D→R,定義域爲下標值0~1000,值域爲1到1000.如果對任意一個數 i,我們把f(i)叫做它的後繼,i叫f(i)的前驅。0只有後繼沒有前驅,其他數字既有後繼也有前驅,重複的那個數字有兩個前驅,我們將利用這些特徵。
規律:從0開始畫一個箭頭指向它的後繼,從它的後繼繼續指向後繼的後繼,這樣,必然會有一個節點指向之前已經出現過的數,即爲重複的數。
利用下標與單元中所存儲的內容之間的特殊關係,進行遍歷訪問單元,一旦訪問過的單元賦予一個標記,利用標記作爲發現重複數字的關鍵。
int find2(int *a) {
int index = 0;
while(true){
if(a[index] < 0 ) {
break;
}
a[index] = 0 - a[index];
index = 0 - a[index];
}
return a[index];
}
(3) 方法三
同樣考慮下標與內容的關係,不過不用標記,而用兩個速度不同的過程來訪問。Slow每次前進一步,fast每次前進兩步。在有環結構中,它們總會相遇。
int find3(int *a) {
int slow =0, fast = 0;
while( true ) {
slow = a[slow];
fast = a[a[fast]];
if(slow == fast){
break;
}
}
return a[slow];
}
(4) 方法四:異或操作
數組a[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
int find4(int *a, int length) {
int result = 0;
for(int i = 1; i <= 1000; i++) {
result ^= i;
}
for(int i=0; i<=1000; i++ ) {
result ^= a[i];
}
return result;
}
摘自http://www.cnblogs.com/answeryi/archive/2012/09/24/2700529.html,部分算法有改動,其中第四個算法借鑑http://blog.csdn.net/imzoer/article/details/8013277