1. 程式人生 > >全排列演算法及其C++實現(轉)

全排列演算法及其C++實現(轉)

第十六章、全排列問題

53.字串的排列。
題目:輸入一個字串,打印出該字串中字元的所有排列。
例如輸入字串abc,則輸出由字元a、b、c 所能排列出來的所有字串
abc、acb、bac、bca、cab 和cba。

    分析:此題最初整理於去年的微軟面試100題中第53題,第二次整理於微軟、Google等公司非常好的面試題及解答[第61-70題] 第67題。無獨有偶,這個問題今年又出現於今年的2011.10.09百度筆試題中。ok,接下來,咱們先好好分析這個問題。

  • 一、遞迴實現
    從集合中依次選出每一個元素,作為排列的第一個元素,然後對剩餘的元素進行全排列,如此遞迴處理,從而得到所有元素的全排列。以對字串abc進行全排列為例,我們可以這麼做:以abc為例
    固定a,求後面bc的排列:abc,acb,求好後,a和b交換,得到bac
    固定b,求後面ac的排列:bac,bca,求好後,c放到第一位置,得到cba
    固定c,求後面ba的排列:cba,cab。程式碼可如下編寫所示:
  1. template <typename T>  
  2. void CalcAllPermutation_R(T perm[], int first, int num)  
  3. {  
  4.     if (num <= 1) {  
  5.         return;  
  6.     }  
  7.     for (int i = first; i < first + num; ++i) {  
  8.         swap(perm[i], perm[first]);  
  9.         CalcAllPermutation_R(perm, first + 1, num - 1);  
  10.         swap(perm[i], perm[first]);  
  11.     }  
  12. }  
    或者如此編寫,亦可:
  1. void Permutation(char* pStr, char* pBegin);  
  2. void Permutation(char* pStr)  
  3. {  
  4.       Permutation(pStr, pStr);  
  5. }  
  6. void Permutation(char* pStr, char* pBegin)  
  7. {  
  8.     if(!pStr || !pBegin)  
  9.         return;  
  10.     if(*pBegin == '\0')  
  11.     {  
  12.         printf("%s\n", pStr);  
  13.     }  
  14.     else
      
  15.     {  
  16.         for(char* pCh = pBegin; *pCh != '\0'; ++ pCh)  
  17.         {  
  18.             // swap pCh and pBegin  
  19.             char temp = *pCh;  
  20.             *pCh = *pBegin;  
  21.             *pBegin = temp;  
  22.             Permutation(pStr, pBegin + 1);    
  23.             // restore pCh and pBegin  
  24.             temp = *pCh;  
  25.             *pCh = *pBegin;  
  26.             *pBegin = temp;  
  27.         }  
  28.     }  
  29. }  
  • 二、字典序排列
    把升序的排列(當然,也可以實現為降序)作為當前排列開始,然後依次計算當前排列的下一個字典序排列。
    對當前排列從後向前掃描,找到一對為升序的相鄰元素,記為i和j(i < j)。如果不存在這樣一對為升序的相鄰元素,則所有排列均已找到,演算法結束;否則,重新對當前排列從後向前掃描,找到第一個大於i的元素k,交換i和k,然後對從j開始到結束的子序列反轉,則此時得到的新排列就為下一個字典序排列。這種方式實現得到的所有排列是按字典序有序的,這也是C++ STL演算法next_permutation的思想。演算法實現如下:
  1. template <typename T>  
  2. void CalcAllPermutation(T perm[], int num)  
  3. {  
  4.     if (num < 1)  
  5.         return;  
  6.     while (true) {  
  7.         int i;  
  8.         for (i = num - 2; i >= 0; --i) {  
  9.             if (perm[i] < perm[i + 1])  
  10.                 break;  
  11.         }  
  12.         if (i < 0)  
  13.             break;  // 已經找到所有排列
  14.         int k;  
  15.         for (k = num - 1; k > i; --k) {  
  16.             if (perm[k] > perm[i])  
  17.                 break;  
  18.         }  
  19.         swap(perm[i], perm[k]);  
  20.         reverse(perm + i + 1, perm + num);  
  21.     }  
  22. }  
  擴充套件:如果不是求字元的所有排列,而是求字元的所有組合,應該怎麼辦呢?當輸入的字串中含有相同的字串時,相同的字元交換位置是不同的排列,但是同一個組合。舉個例子,如果輸入abc,它的組合有a、b、c、ab、ac、bc、abc

轉自
http://blog.csdn.net/v_july_v/article/details/6879101