找出數組中唯一的重復元素※
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開始畫一個箭頭指向它的后繼,從它的后繼繼續指向后繼的后繼,這樣,必然會有一個節點指向之前已經出現過的數,即為重復的數。
利用下標與單元中所存儲的內容之間的特殊關系,進行遍歷訪問單元,一旦訪問過的單元賦予一個標記,利用標記作為發現重復數字的關鍵。代碼如下:
void FindRepeat(int array[], int length)
{
int index = 0;
while ( true )
{
if ( array[index]<0 )
break;
array[index] *= -1; //訪問過,變成相反數
index=array[index]*(-1);
}
cout<<"The repeat number is "<< -array[index] <<endl;
}
(3) 方法三
同樣考慮下標與內容的關系,不過不用標記,而用兩個速度不同的過程來訪問。Slow每次前進一步,fast每次前進兩步。在有環結構中,它們總會相遇。
void FindRepeat(int array[], int length)
{
int slow=fast= 0;
while ( true ) {
slow = array[slow];
fast = array[array[fast]];
if( slow == fast )
break;
}
fast = 0;
while( true) {
slow= array[slow];
fast =array[fast];
if( slow == fast )
break;
}
cout<<"The repeat number is "<< array[slowendl;
}
(4) 方法四:異或操作
void FindRepeat(int array[], int length)
{
int result = 0;
for(int i=1;i<=1000;i++)
result ^= i;
for(int i=0;i<=1000;i++)
result ^= array[i];
cout << result << endl;
}