Java下實現無重字串的全排列(遞迴和回溯方法)
阿新 • • 發佈:2019-03-30
給定一個無重複字元的字串陣列,實現它的全排列
一、採用遞迴實現全排列
思想是從第一個字元開始排,一直排到只有一個字元那麼就說明一個排列完成了。否則就交換當前第k個字元和第i個字元(這裡的第i個字元指的是k後面所有字元的其中一個,例如abcd,當a固定,對bcd排列,那就要將b和c、b和d依次交換),再對k+1到m進行排列。當返回成功後就再將第k個字元和第i個字元再交換回來。
遞迴的終止條件就是k=m
k 為起始下標,m為結束下標,下面的函式用於返回從k到m的全排列
list[]用於儲存待排列的字串
public static void perm(char list[], int k, int m) { if(k == m) { System.out.println(list); } else { for(int i = k; i <= m; i++) { //交換第k個字元和第i個字元,因為java是按值傳遞,所以我就沒用函式交換 char temp; temp = list[k]; list[k] = list [i]; list[i] = temp; //再對k後面的字串進行全排列 perm(list, k+1, m); //排列完後再換回來繼續下一層迴圈 temp = list[k]; list[k] = list [i]; list[i] = temp; } } }
二、採用回溯實現全排列
思想是深度優先搜尋,比如要找12345的全排列的一種排列12345
第一層,有五種選擇,1、2、3、4、5,五個元素都標記沒被使用過
第二層,1下面有四種2、3、4、5, 1被標記使用過
第三層,2下面有三種3、4、5, 1、2被標記使用過
第四層,3下面有兩種4、5, 1、2、3被標記使用過
第五層,4下面就只能排5了,1、2、3、4被標記使用過
這時就找到了一種排列12345,然後將5標記為沒被使用過,返回到上一層4,將4也標記為沒使用過,但5可以選,然後再選4,這樣就實現了回溯,就可以得到所有解了。
具體程式碼如下:
引數n表示第幾層,list是一個char型的陣列,存放了待全排列的字串
used[]是一個bool型的陣列,具體下標對應的真假值判斷那個下標的字元有沒有被使用過,vector[]是一個整型陣列,用於存放一個排列的解,amount用於計數,result函式用於將解向量輸出成排列。
public static void perm(char list[], int k, int m) {
if(k == m) {
System.out.println(list);
}
else {
for(int i = k; i <= m; i++) {
//交換
char temp;
temp = list[k];
list[k] = list [i];
list[i] = temp;
//返回k之後的全排列
perm(list, k+1, m);
//交換回來
temp = list[k];
list[k] = list [i];
list[i] = temp;
}
}
}
完整的可以通過輸入字串(要保證無重複字元)來返回所有排列的測試程式碼如下:
import java.nio.file.attribute.AclEntryPermission;
import java.util.Arrays;
import java.util.Scanner;
public class Generator {
static boolean used[];//存放字元是否被使用過
static char list[];//存放字串
static int vector[];//存放當前排列的解向量
static int amount = 0;//用於計數
public static void main(String[] args) {
// 使用者輸入字串
String str;
Scanner input = new Scanner(System.in);
str = input.next();
list = str.toCharArray();
used = new boolean[str.length()];
vector = new int[str.length()];
//perm(list, 0, str.length()-1); //用遞迴實現全排列
Aperm(0);//用回溯實現全排列,從第0層開始
}
public static void perm(char list[], int k, int m) {
if(k == m) {
System.out.println(list);
}
else {
for(int i = k; i <= m; i++) {
//交換
char temp;
temp = list[k];
list[k] = list [i];
list[i] = temp;
//返回k之後的全排列
perm(list, k+1, m);
//交換回來
temp = list[k];
list[k] = list [i];
list[i] = temp;
}
}
}
public static void Aperm(int n) {
//第0層有i.length種選擇
for(int i = 0; i < list.length; i++) {
if(!used[i]) {
vector[n] = i;//記錄當前解
used[i] = true;//當前元素被使用
if(n!= list.length - 1)
Aperm(n+1);//若不是最後一層則返回下一層的
else//否則就是最後一層,那麼就找到了一種排列,輸出
System.out.println(amount++ +": "+ result(vector));
//很重要,返回到上一層就得取消標記
used[i] = false;
}
}
}
public static String result(int vector[]) {
String string = "";
for(int i = 0; i < vector.length; i++) {
string += list[vector[i]];
}
return string;
}
}