1. 問題描述
取值爲[1,n-1]含n個元素的整數數組至少存在一個重複數,O(n)時間內找出其中任意一個重複數。如a[]={1,2,2,4,5,4},則2和4均是重複元素。
2. 解決方案
<單鏈表存在環> 第三種解決方案有很強的技巧性。“判斷單鏈表是否存在環”是一個非常經典的問題,同時單鏈表可以採用數組實現,此時每個元素值作爲next指針指向下一個元素。該題可以轉化爲“已知一個單鏈表中存在環,找出環的入口點”。
該題思路如下:將a[i]看做第i個元素的索引,即:a[i]->a[a[i]]->a[a[a[i]]]->a[a[a[a[i]]]]->….最終形成一個單鏈表,由於數組a中存在重複元素,則一定存在一個環,且環的入口元素即爲重複元素。
該題的關鍵在於,數組a的大小時n,而元素的範圍是[1,n-1],所以a[0]不會指向自己,進而不會陷入錯誤的自循環。如果元素的範圍中包含0,則該題不可直接採用該方法。
代碼如下:
int find_duplicated_integer2(int a[], int n) {
int x, y;
x = y = 0;
do {
x = a[a[x]]; //x一次走兩步
y = a[y]; //y一次走一步
} while(x != y); //找到環中的一個點
x = 0;
do {
x = a[x];
y = a[y];
} while(x != y); //找到入口點
return x;
}
一開始不明白爲什麼分這樣的兩趟進行,後來用數學式推導了一下是對的。
證明上述方法的正確性:
設環外、環上元素個數分別爲a,b。
進行第一次循環,初始狀態x=0,y=0。x,y從環外開始走,第a+1步進入環後必定在環上追趕而相遇,設相遇時y走了a+m步,則x走了2(a+m)步=a+(a+2m).
a+m,a+(a+2m)在環上同一位置(但並不一定是環的入口a+1),那麼兩者之差a+m是環上元素個數b的倍數,即a+m可被b整除,設a+m=nb.
進行第二次循環,初始狀態x=0,y=a+m。x從環外開始走,第a+1步到達環的入口;此時y到達a+m + a+1=a+nb+1,同樣也是環的入口。故兩者在環的入口相遇。
入口點元素即爲數組重複元素。