一,問題描述
給定一個字符串,求出該字符串的全排列。
比如:"abc"的全排列是:abc、acb、bac、bca、cab、cba
二,實現思路
采用遞歸的方式求解。每次先選定一個字符,然后進行“若干次”交換,求出在選定這個字符的條件下,所有的全排列,並把字符“復位”再交換回來。至此,一趟全排列完成。第二趟,選定下一個字符,然后進行“若干次”交換,求出在選定這個字符的條件下,所有的全排列,並把字符“復位”再交換回來。.....
就類似於:(參考網上的解釋如下:)
設R={r1,r2,...rn}是要進行排列的n個元素.Ri=R-{ri}.集合X中元素的全排列記為
Perm(X).(ri)Perm(X)表示在全排列Perm(X)的每一個排列前加上前綴ri得到的排列
R的全排列可歸納定義如下:
當n=1時,Perm(R)=(r),其中r是集合R中唯一的元素;
當r>1時,Perm(R)由(r1)Perm(r1),(r2)Perm(r2).....(rn)Perm(rn)構成.
全排列就是從第一個數字起每個數分別與它后面的數字交換
去重的全排列就是從第一個數字起每個數分別與它后面非重復出現的數字交換,
用編程的話描述就是第i個數與第j個數交換時,要求[i,j)中沒有與第j個數相等的數。
代碼實現如下:使用一個LinkedList<String>保存每一種排列,如果字符串中出現有重復的字符,則此方法會求出 重復的排列數,因而LinkedList<String>會保存重復的排列。
import java.util.Collections; import java.util.LinkedList; public class Permutation { public static void allPermutation(String str){ if(str == null || str.length() == 0) return; //保存所有的全排列 LinkedList<String> listStr = new LinkedList<String>(); allPermutation(str.toCharArray(), listStr, 0); print(listStr);//打印全排列 } private static void allPermutation(char[] c, LinkedList<String> listStr, int start){ if(start == c.length-1) listStr.add(String.valueOf(c)); else{ for(int i = start; i <= c.length-1; i++) { swap(c, i, start);//相當於: 固定第 i 個字符 allPermutation(c, listStr, start+1);//求出這種情形下的所有排列 swap(c, start, i);//復位 } } } private static void swap(char[] c, int i, int j){ char tmp; tmp = c[i]; c[i] = c[j]; c[j] = tmp; } private static void print(LinkedList<String> listStr) { Collections.sort(listStr);//使字符串按照'字典順序'輸出 for (String str : listStr) { System.out.println(str); } System.out.println("size:" + listStr.size()); } //hapjin test public static void main(String[] args) { // allPermutation("hapjin"); allPermutation("abc"); } }
如果要想讓重復的排列只保存一次,有兩種方式:①改進算法,不生成重復的排列 ②用HashSet來保存排列
那當字符串中出現重復的字符時,如何生成不重復的排列?---去重的全排列就是從第一個數字起每個數分別與它后面非重復出現的數字交換
代碼實現如下:(當有重復字符時,也可生成所有正確的排列(排列不會重復))
public class Permutation { public static void allPermutation(String str){ if(str == null || str.length() == 0) return; //保存所有的全排列 LinkedList<String> listStr = new LinkedList<String>(); allPermutation(str.toCharArray(), listStr, 0); print(listStr);//打印全排列 } private static void allPermutation(char[] c, LinkedList<String> listStr, int start){ if(start == c.length-1) listStr.add(String.valueOf(c));//System.out.println(String.valueOf(c)); else{ for(int i = start; i <= c.length-1; i++) { //只有當沒有重疊的字符 才交換 if(!isSwap(c, start, i)) { swap(c, i, start);//相當於: 固定第 i 個字符 allPermutation(c, listStr, start+1);//求出這種情形下的所有排列 swap(c, start, i);//復位 } } } } private static void swap(char[] c, int i, int j){ char tmp; tmp = c[i]; c[i] = c[j]; c[j] = tmp; } private static void print(LinkedList<String> listStr) { Collections.sort(listStr);//使字符串按照'字典順序'輸出 for (String str : listStr) { System.out.println(str); } System.out.println("size:" + listStr.size()); } //[start,end) 中是否有與 c[end] 相同的字符 private static boolean isSwap(char[] c, int start, int end) { for(int i = start; i < end; i++) { if(c[i] == c[end]) return true; } return false; } //hapjin test public static void main(String[] args) { // allPermutation("hapjin"); allPermutation("aba"); } }
上面的實現將所有的排列順序都保存到LinkedList<String>了,這是要注意的。當然也可以不保存排列的順序,直接輸出(allPermutation方法)。
if(start == c.length-1) listStr.add(String.valueOf(c));//保存排列 //System.out.println(String.valueOf(c));//不保存排列,直接輸出
參考資料:全排列和全組合的實現
原文:http://www.cnblogs.com/hapjin/p/5757810.html