問題
輸入一個字符串,打印出該字符串中字符的所有排列。例如輸入字符串abc,則輸出由字符a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab和cba
思路
1.這是典型的遞歸求解問題,遞歸算法有四個特性:
- 必須有可達到的終止條件,否則程序陷入死循環
- 子問題在規模上比原問題小
- 子問題可通過再次遞歸調用求解
- 子問題的解應能組合成整個問題的解
2.對於字符串的排列問題:
如果能生成n-1個元素的全排列,就能生成n個元素的全排列。對於只有一個元素的集合,可以直接生成全排列。所以全排列的遞歸終止條件很明確,只有一個元素時。我們可以分析一下全排列的過程:
(1)首先,我們固定第一個字符a,求後面兩個字符bc的排列
(2)當兩個字符bc排列求好之後,我們把第一個字符a和後面的b交換,得到bac,接着我們固定第一個字符b,求後面兩個字符ac的排列
(3)現在是把c放在第一個位置的時候了,但是記住前面我們已經把原先的第一個字符a和後面的b做了交換,爲了保證這次c仍是和原先處在第一個位置的a交換,我們在拿c和第一個字符交換之前,先要把b和a交換回來。在交換b和a之後,再拿c和處於第一位置的a進行交換,得到cba。我們再次固定第一個字符c,求後面兩個字符b、a的排列
(4)既然我們已經知道怎麼求三個字符的排列,那麼固定第一個字符之後求後面兩個字符的排列,就是典型的遞歸思路了
下面這張圖很清楚的給出了遞歸的過程:
去掉重複的全排列
如果輸入字符有重複字符,就是會造成重複數據的輸出,例如abb這種字符串,基於上訴討論結果如圖:
由於全排列就是從第一個數字起,每個數分別與它後面的數字交換,我們先嚐試加個這樣的判斷——如果一個數與後面的數字相同那麼這兩個數就不交換了。例如abb,第一個數與後面兩個數交換得bab,bba。然後abb中第二個數和第三個數相同,就不用交換了。但是對bab,第二個數和第三個數不同,則需要交換,得到bba。由於這裏的bba和開始第一個數與第三個數交換的結果相同了,因此這個方法不行。
換種思維,對abb,第一個數a與第二個數b交換得到bab,然後考慮第一個數與第三個數交換,此時由於第三個數等於第二個數,所以第一個數就不再用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換可以解決bba。此時全排列生成完畢!
#include <stdio.h> #include <stdlib.h> #include <string.h> struct seq{ char str[7]; }; struct seq seqs[800]; int count; int is_swap(char *str, int begin, int k){ ///判斷是否需要交換 int i, flag; for (i = begin, flag = 1; i < k; i ++) { if (str[i] == str[k]) { flag = 0; break; } } return flag; } void swap(char *str, int a, int b) ///交換字母 { char temp; temp = str[a]; str[a] = str[b]; str[b] = temp; } void permutation_process(char *name, int begin, int end) { int k; if (begin == end - 1) ///遞歸出口 { strcpy(seqs[count].str, name); ///進行保存,用於後續的輸出 count ++; } else { for (k = begin; k < end; k ++) { if (is_swap(name, begin, k)) ///判斷是否要進行交換。如果沒有交換,則忽略這種情況 { swap(name, k, begin); ///字母交換 permutation_process(name, begin + 1, end); ///對[begin + 1,end)進行全排列 swap(name, k, begin); ///字母換回 } } } } int compare(const void *p, const void *q) { const char *a = (char*)p; const char *b = (char*)q; return strcmp(a, b); } int main() { char name[7]; ///保存要進行全排列的字符串,長度應較小(小於7) int i, len; while (scanf("%s", name) != EOF) { count = 0; len = strlen(name); permutation_process(name, 0, len); ///全排列 qsort(seqs, count, sizeof(seqs[0]), compare); ///將生成的全排列排序 for (i = 0; i < count; i ++) printf("%s\n", seqs[i].str); printf("\n"); } return 0; }
輸入:abca
輸出: