如何用C表示排列組合?

來源:互聯網
上載者:User

標籤:style   blog   color   io   for   sp   div   問題   log   

問題來自《Linux C一站式編程》,是個挺有意思的題目。

2、定義一個數組,編程列印它的全排列。比如定義:

#define N 3 int a[N] = { 1, 2, 3 };

則運行結果是:

$ ./a.out1 2 3 1 3 2 2 1 3 2 3 1 3 2 1 3 1 2

程式的主要思路是:

  1. 把第1個數換到最前面來(本來就在最前面),準備列印1xx,再對後兩個數2和3做全排列。
  2. 把第2個數換到最前面來,準備列印2xx,再對後兩個數1和3做全排列。
  3. 把第3個數換到最前面來,準備列印3xx,再對後兩個數1和2做全排列。

可見這是一個遞迴的過程,把對整個序列做全排列的問題歸結為對它的子序列做全排列的問題,注意我沒有描述Base Case怎麼處理,你需要自己想。
你的程式要具有通用性,如果改變了N和數組a的定義(比如改成4個數的數組),其它代碼不需要修改就可以做4個數的全排列(共24種排列)。

完成了上述要求之後再考慮第二個問題:如果再定義一個常量M表示從N個數中取幾個數做排列(N==M時表示全排列),原來的程式應該怎麼改?

最後再考慮第三個問題:如果要求從N個數中取M個數做組合而不是做排列,就不能用原來的遞迴過程了,想想組合的遞迴過程應該怎麼描述,編程實現它。

不考慮數組元素相同的情況,我們可以按照題目提供的思路寫出如下代碼:

#include <stdio.h>#define N 3int a[N];void perm(int); /*求數組的全排列 */void print();void swap(int, int);int main(){    int i;    for(i = 0; i < N; ++i){        a[i] = i + 1;    }    perm(0);}void perm(int offset){    int i, temp;    if(offset == N-1){  // BaseCase        print();        return;    }else{        for(i = offset;i < N; ++i){            swap(i, offset);//交換首碼            perm(offset + 1);//遞迴            swap(i, offset);//將首碼換回來,繼續做前一次排列        }    }}void print(){    int i;    for(i = 0; i < N; ++i)        printf(" %d ",a[i]);    printf("\n");}   void swap(int i, int offset){    int temp;    temp = a[offset];    a[offset] = a[i];    a[i] = temp;}

 

如果平常遞迴寫的不多的話,這段代碼還是很容易寫錯的(沒錯,我就是在說我自己)。
在perm函數遞迴調用自己之後記得把元素位置交換回去,保證回溯時條件一致。

然後看第二個問題,這是更加一般的排列。仔細觀察上面的代碼,把特殊推導到一般,主要修改如下(用注釋符標出):

#include <stdio.h>#define N 4#define M 2   // 取出M個元素進行排列,預設M<=Nvoid print(){      int i;      for(i = 0; i < M; ++i)  // N->M,列印a裡前M個排列元素        printf(" %d ",a[i]);      printf("\n");  }void perm(int offset){      int i;      if(offset == M){  // N->M,排列到M個數時遞迴到達BaseCase        print();          return;      }else{          for(i = offset;i < N; ++i){              swap(i, offset);            perm(offset + 1);            swap(i, offset);         }      }  } 

 

再來看組合,同樣用要求用遞迴解決,如果相關概念沒有搞得很清楚,加上上面寫的排列的代碼,很容易寫不出來(沒錯,說的還是我),然而代碼其實很簡單,不過遞迴確實更加複雜。

void comb(int n, int m){    int i;    if (m == 0) {        print();        return;    } else {        for (int i = n-1; i >= 0; --i)        {            b[m-1] = a[i];            comb(i, m-1);        }    }}

 

複雜之處在於,排列都是(n->n-1)這樣的遞迴,然而組合這裡是(n->i,m->m-1)這樣非規律的遞迴調用,因為i是個變數。
但是組合的演算法描述很簡單,假設有一個兩兩元素互不相同的N長數組a,從數組尾端依次取M個數(b1,b2,···,bm)成為一個組合,並且滿足條件:如果i>j,那麼bi在a中的下標一定小於bj。

簡單來說,就是從後往前取數組裡的M個數,只有保持這樣的偏序關係才能保證不會重複的群組合。

如何用C表示排列組合?

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.