算法:根據輸入的字符串,生成其全排列字符串集合

最近遇到一個很有意思的問題,那就是生成指定字符串的全排列集合。什麼意思呢,我舉兩個例子你就明白了:

  1. 字符串 “ABC” 的全排列集合:ABC, ACB, BAC, BCA, CAB, CBA
  2. 字符串 “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]
發佈了67 篇原創文章 · 獲贊 14 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章