2016-02-27 95 views
3

我想提高我的算法知識的0/1排序順序,我試圖解決從Skiena的算法設計手冊以下問題:算法:只使用比較

4-26考慮的問題使用比較對n 0和1的序列進行排序。對於x和y兩個值的每個比較,算法會學習哪個x保持不變。 (a)給出一個算法,在最壞情況下對n - 1個比較進行排序。證明你的算法是最優的。

(b)在平均情況下給出一種算法對2n/3個比較進行排序(假設每個n個輸入爲0或1,概率相等)。證明你的算法是最優的。

這是我(一)解決方案:

void sort(int arr[], int length) { 
    int last_zero = -1; 
    for (int i = 1; i < length; i++) { 
     if (arr[i] < arr[i-1]) { 
      int tmp = arr[i]; 
      arr[i] = arr[++last_zero]; 
      arr[last_zero] = tmp; 
     } else if (arr[i] > arr[i-1]) { 
      last_zero = i-1; 
     } 
    } 
    return; 
} 

有沒有更好的方式來做到這一點?

我不知道如何處理(b)部分。有一個類似的問題here,但我不明白那裏的解釋。

編輯:顯然這被認爲太模糊,所以讓我根據回覆進行跟進。

我正在追蹤@ amit的回覆。我不太明白這部分:

(!!!!!這樣I_K = i_h爲K = H,I_K =對於k = i_h小時,I_K = j_h 對所有K,H) 。

無論如何,我想我通常理解你提出的解決方案(b)的想法。然而,當我試圖爲它編寫代碼時,我發現很難完成。這是我迄今爲止,並根據我的測試它成功地對所有(0,1)和(1,0)對進行排序並將相等的對推到數組的末尾,所以我最終得到{全零,全1 ,等於對}。我試圖實際移動數組元素,而不是計數0和1的數字。我被困在如何從我至今着手:

int compare(int a, int b); 
void shift_right(int arr[], int start, int end); 
int prob_4_26_b(int arr[], int length) { 
    int last_zero = -1; 
    int last_one = -1; 
    for (int i = 0; i < length; i += 2) { 
     int tmp = compare(arr[i], arr[i+1]); 
     int cur_zero, cur_one; 
     if (tmp == 0) { 
      continue; 
     } 

     cur_zero = i; 
     cur_one = i + 1; 
     /* We have a 0 and a 1 */ 
     /* If this is the first zero we have put it at index 0 
     and shift everything from index 0 to cur_zero-1 right by 1. 
     last_zero = 0 and if there are ones last_one++ */ 
     if (last_zero == -1) { 
      int start = 0; 
      int end = cur_zero - 1; 
      shift_right(arr, start, end); 
      arr[0]=0; 
      last_zero = 0; 
      if (last_one != -1) { 
       last_one++; 
      } 
     } 
     /* If this is not the first zero we have then put it at 
     last_zero+1 and shift everything from last_zero+1 to cur_zero-1 
     right by 1. last_zero++ and if we have ones last_one++. */ 
     else { 
      int start = last_zero + 1; 
      int end = cur_zero - 1; 
      shift_right(arr, start, end); 
      arr[last_zero+1] = 0; 
      last_zero++; 
      if (last_one != -1) { 
       last_one++; 
      } 
     } 

     /* If this is the first one we have put it after the last_zero. 
     Shift everything from last_zero+1 to cur_one-1 right by 1. 
     last_one = last_zero+1. */ 
     if (last_one == -1) { 
      int start = last_zero + 1; 
      int end = cur_one-1; 
      shift_right(arr, start, end); 
      arr[last_zero+1] = 1; 
      last_one = last_zero + 1; 
     } 
     /* If this is not the first one we have put it at last_one+1 
     and shift everything from last_one+1 to cur_one-1 right by 1. 
     last_one++ */ 
     else { 
      int start = last_one + 1; 
      int end = cur_one - 1; 
      shift_right(arr, start, end); 
      arr[last_one+1] = 1; 
      last_one++; 
     } 
    } 
    return 0; 
} 

void shift_right(int arr[], int start, int end) { 
    for (int i = end; i >=start; i--) { 
     arr[i+1] = arr[i]; 
    } 
} 

int compare(int a, int b) { 
    if (a < b) 
     return -1; 
    else if (a > b) 
     return 1; 
    else 
     return 0; 
} 

回答

2

爲了做第二部分,你需要首先實現檢查comp(a[i], a[i+1]),並comp(a[i+1], a[i+2])不無關係。第一個的答案可能會幫助你獲得第二個答案。

要利用它,首先將序列拆分爲成對,(a[i1],a[j1]), (a[i2],a[j2]),..., (a[i_n/2], a[j_n/2])。 (使得對於k,h,i_k!= i_h,對於k!= h,i_k!= i_h,並且對於所有的k,h,i_k!= j_h)。

比較每個這樣的對。平均而言(假設這些比特是均勻分佈的),您將得到n/4的結論性答案a[i] < a[j]或其他方式,而對於剩下的n/4,您將具有平等性。因此,首先你可以很容易地對那些具有決定性答案的人進行排序(開始時較小,結束時較大)。

接下來,您將遞歸地調用提醒中的算法。但是,如果你知道對於某些i,ja[i] = a[j],你就不需要爲兩者都得到答案。其中一人的答案也會告訴你第二個人的價值。
這意味着你可以遞歸調用只有n/4個元素,而不是n/2(平均)。

停止條款將是當你有一個單一的元素,只是比較它與0知道它的價值。

這給你的複雜功能:

T(1) = 1 
T(n) = n/2 + T(n/4) 

一些數學後找到這個遞推公式(或consulting with wolfram alpha)緊密的形式,你會發現,T(n) = (2n+1)/3

0

我不會給你一個完整的解決方案,但也許這足以讓您指向正確的方向。該問題可能變得有點更加清晰,同時陳述問題時,從字面上什麼比較呢:

int compare(int a,int b){ 
    if (a>b) return 1; 
    if (b>a) return -1; 
    return 0; 
} 

下一步是要認識到,你實際上只需要算零(或1)進行排序陣列。然而,只要你比較的數字是平等的,你不知道,如果它是零或一的(否則你就只需要N/2「比較」):

typedef std::vector<int> T; 
int count(const T& vect) { 
    int count = 0; 
    int temp_i = -1; 
    int temp_count = 0;   
    for (i=0;i<vect.size();i=i+2){ 
     int x = abs(compare(vect[i],vect[i+1])); // (1) 
     if (x==1) count++; 
     if (x==0) { 
      if (temp==-1) { 
       temp_i = i; 
       temp_count = 2; 
      } else { 
       int x = compare(vect[i],vect[temp_i])); // (2) 
       if (x==1) {     // 2 ones and some zeros 
        count += 2; 
        temp_count = 0; 
        temp_i = -1; 
       } else if (x==-1) {   // 2 zeros and some ones 
        count += temp_count; 
        temp_count = 0; 
        temp_i = -1; 
       } else {      // all the same 
        temp_count += 2; 
       } 
     } 
    } 
} 

我基本上比較成對的數字,當他們是一樣的,我不知道它是零還是零(否則這個問題是微不足道的),我記得索引與我遇到的下一對相同的數字進行比較。當他們再次相同時,我只需要記住有多少對我遇到,直到我有一對不等於另一對。 我甚至沒有嘗試編譯代碼,但它不是最優的。它只適用於數組的大小,我只是意識到當循環結束時我忘了添加temp_count。一旦我有更多的時間,我會解決它。然而,看到複雜度如何降低就足夠了:

第一個比較(1)正好執行n/2次,對於平均輸入,在50%的情況下需要進行第二次比較。不是真正的被要求的2/3 n,但它在正確的方向;)。