[字符串] --- 字符串的排列(剑指 Offer 38)
【試題描述】
輸入一個字符串,打印出該字符串中字符的所有排列。你可以以任意順序返回這個字符串數組,但里面不能有重復元素。
例如輸入字符串abc,則打印出a,b,c所能排列出來的所有字符串abc,acb,bac,bca,cab,cba。
劍指 Offer 38. 字符串的排列 https://leetcode-cn.com/problems/zi-fu-chuan-de-pai-lie-lcof/
?
我們以三個字符abc為例來分析一下求字符串排列的過程。首先我們固定第一個字符a,求后面兩個字符bc的排列。當兩個字符bc的排列求好之后,我們把第一個字符a和后面的b交換,得到bac,接著我們固定第一個字符b,求后面兩個字符ac的排列。現在是把c放到第一位置的時候了。記住前面我們已經把原先的第一個字符a和后面的b做了交換,為了保證這次c仍然是和原先處在第一位置的a交換,我們在拿c和第一個字符交換之前,先要把b和a交換回來。在交換b和a之后,再拿c和處在第一位置的a進行交換,得到cba。我們再次固定第一個字符c,求后面兩個字符b、a的排列。
既然我們已經知道怎么求三個字符的排列,那么固定第一個字符之后求后面兩個字符的排列,就是典型的遞歸思路了。
為方便起見,用123來示例下。123的全排列有123、132、213、231、312、321這六種。首先考慮213和321這二個數是如何得出的。顯然這兩個都是123中的1與后面兩數交換得到的。然后可以將123的第二個數和每三個數交換得到132。同理可以根據213和321來得231和312。因此可以知道——全排列就是從第一個數字起每個數分別與它后面的數字交換。找到這個規律后,遞歸的代碼就很容易寫出來了:
對于一個n 位的字符串來講,它是n-1位字符串的排列 加上 沒有在 n -1 位字符串里那個字符 的排列。
有點難理解,用例子說明:對于字符串ABC來講,它所有的排列就是 A + BC 的排列 加上 B + AC 的排列,再加上 C + AB的排列。而BC的排列是 B + C 的排列 加上 C + B 的排列。所以,對一個字符串,我們從中去一個值,然后求剩余部分的排列,然后把它們再組合在一起。所有,代碼如下
【參考代碼】
如果字符串中有重復字符的話,這個方法肯定不會符合要求的
public static void permutation(char[] arr, int begin) {// if pBegin points to the end of string,// this round of permutation is finished,// print the permuted stringif (begin == arr.length)System.out.println(Arrays.toString(arr));else{for (int i = begin; i < arr.length; i++){swap(arr, i, begin);permutation(arr, begin + 1);// restore pCh and pBegin// PS:改變字符串順序后必須還原回來!swap(arr, i, begin);}} }public static void swap(char[] arr, int i, int j) {char temp = arr[i];arr[i] = arr[j];arr[j] = temp; }?
二、去掉重復的全排列的遞歸實現
由于全排列就是從第一個數字起每個數分別與它后面的數字交換。我們先嘗試加個這樣的判斷——如果一個數與后面的數字相同那么這二個數就不交換了。如122,第一個數與后面交換得212、221。然后122中第二數就不用與第三個數交換了,但對212,它第二個數與第三個數是不相同的,交換之后得到221。與由122中第一個數與第三個數交換所得的221重復了。所以這個方法不行。
換種思維,對122,第一個數1與第二個數2交換得到212,然后考慮第一個數1與第三個數2交換,此時由于第三個數等于第二個數,所以第一個數不再與第三個數交換。再考慮212,它的第二個數與第三個數交換可以得到解決221。此時全排列生成完畢。
這樣我們也得到了在全排列中去掉重復的規則——去重的全排列就是從第一個數字起每個數分別與它后面非重復出現的數字交換。
?三、全排列的非遞歸實現
??? 要考慮全排列的非遞歸實現,先來考慮如何計算字符串的下一個排列。如"1234"的下一個排列就是"1243"。只要對字符串反復求出下一個排列,全排列的也就迎刃而解了。
如何計算字符串的下一個排列了?來考慮"926520"這個字符串,我們從后向前找第一雙相鄰的遞增數字,"20"、"52"都是非遞增的,"26 "即滿足要求,稱前一個數字2為替換數,替換數的下標稱為替換點,再從后面找一個比替換數大的最小數(這個數必然存在),0、2都不行,5可以,將5和2交換得到"956220",然后再將替換點后的字符串"6220"顛倒即得到"950226"。
對于像“4321”這種已經是最“大”的排列,采用STL中的處理方法,將字符串整個顛倒得到最“小”的排列"1234"并返回false。
這樣,只要一個循環再加上計算字符串下一個排列的函數就可以輕松的實現非遞歸的全排列算法。按上面思路并參考STL中的實現源碼,不難寫成一份質量較高的代碼。值得注意的是在循環前要對字符串排序下,可以自己寫快速排序的代碼
轉:http://blog.csdn.net/zz198808/article/details/7657168
總結:
1、全排列就是從第一個數字起每個數分別與它后面的數字交換。
2、去重的全排列就是從第一個數字起每個數分別與它后面非重復出現的數字交換。
3、全排列的非遞歸就是由后向前找替換數和替換點,然后由后向前找第一個比替換數大的數與替換數交換,最后顛倒替換點后的所有數據。
?
?
總結
以上是生活随笔為你收集整理的[字符串] --- 字符串的排列(剑指 Offer 38)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 看了热播剧《狂飙》,我学会了用 PS 做
- 下一篇: 【IT笔试面试题整理】寻找二叉树两节点的