最近遇到一個很有意思的問題,那就是生成指定字符串的全排列集合。什麼意思呢,我舉兩個例子你就明白了:
- 字符串 “ABC” 的全排列集合:ABC, ACB, BAC, BCA, CAB, CBA
- 字符串 “ABB” 的全排列集合:ABB, BAB, BBA
相信你已經看明白了,就是組成字符串的所有字符重新打亂順序,然後生成其所有排列形式的新字符串的集合。如果字符串裏面有重複的字符,則結果集合需要去重。這個算法我還是打算用遞歸來實現,不得不說遞歸真的是太好用了!下面是實現這個算法的源碼:
import java.util.*;
/**
* @author LiYang
* @ClassName StringFullPermutation
* @Description 字符串的全排列生成工具類
* @date 2019/12/23 17:11
*/
public class StringFullPermutation {
/**
* 全排列字符串的遞歸生成方法
* @param prefix 當前的前綴字符串
* @param next 下一個字符
* @param remain 剩下待選擇排列的字符串集合
* @param all 收集所有排列結果的集合
*/
private static void generate(String prefix, String next, List<String> remain, Set<String> all) {
//將當前的前綴字符串和下一個字符結合
//並更新爲最新的前綴字符串
String newPrefix = prefix + next;
//如果剩下的排列字符串已經沒有了
if (remain.size() == 0) {
//證明已經完成一種排列,加入到相應集合
all.add(newPrefix);
//結束當前遞歸分支
return;
}
//如果還有剩下的字符,則遍歷
for (int i = 0; i < remain.size(); i++) {
//深拷貝剩下的字符集合
List<String> newRemain = copyList(remain);
//將當前的字符移除,並作爲最新的下一個字符
String newNext = newRemain.remove(i);
//將新的前綴字符串、最新的下一個字符、
//最新的剩下的字符集合,結果統計集合入參
//並遞歸調用本方法,繼續往後進行全排列
generate(newPrefix, newNext, newRemain, all);
}
}
/**
* 全排列字符串的驅動方法
* @param input 入參的字符串
* @param all 收集全排列的集合
*/
private static void generate(String input, Set<String> all) {
//獲得輸入字符串的字符數組
char[] chars = input.toCharArray();
//字符的字符串集合
List<String> charList = new ArrayList<>();
//將輸入字符串的字符數組,轉化爲字符的字符串集合
//以供後面的遞歸方法入參
for (int i = 0; i < chars.length; i++) {
charList.add(Character.toString(chars[i]));
}
//將字符的字符串集合遍歷
for (int i = 0; i < charList.size(); i++) {
//每個遞歸分支拷貝一份
List<String> remain = copyList(charList);
//移除當前的字符,作爲下一個字符
String next = remain.remove(i);
//因爲剛開始調用,前綴字符串爲""
//且剛開始的下一個字符的字符串就是當前字符
//剩下的字符集合,就是移除當前字符後的集合
//同時放入全排列統計集合,遞歸調用方法
generate("", next, remain, all);
}
}
/**
* 深拷貝List<String>
* @param source 拷貝源
* @return 拷貝副本
*/
private static List<String> copyList(List<String> source) {
//拷貝副本
List<String> copy = new ArrayList<>();
//逐一拷貝
for (String str : source) {
copy.add(str);
}
//返回拷貝副本
return copy;
}
/**
* 生成輸入字符串的所有字符的全排列集合
* @param input 輸入的字符串
* @return 輸入字符串的所有字符全排列集合,升序
*/
public static Set<String> generate(String input) {
//用TreeSet,排序生成的字符串全排列集合
//這裏考慮到字符串的順序,所以用TreeSet
//如果不在乎順序,也可以用HashSet
Set<String> all = new TreeSet<>();
//運行生成的方法,TreeSet除了排序還有去重的作用
generate(input, all);
//返回輸入字符串的所有字符全排列升序集合
return all;
}
/**
* 驗證字符串全排列的算法
* @param args
*/
public static void main(String[] args) {
//測試並打印"ABC"字符串的全排列集合,驗證算法(不存在重複)
System.out.println("\"ABC\"的全排列:" + generate("ABC"));
//測試並打印"ABB"字符串的全排列集合,驗證算法(存在重複,Set會自動去重)
System.out.println("\"ABB\"的全排列:" + generate("ABB"));
}
}
運行StringFullPermutation類的main方法,控制檯輸出如下結果,算法測試通過:
"ABC"的全排列:[ABC, ACB, BAC, BCA, CAB, CBA]
"ABB"的全排列:[ABB, BAB, BBA]