(不懂)字符串的排列

輸入一個字符串,按字典序打印出該字符串中字符的全部排列。例如輸入字符串 abc,則打印出由字符 a, b, c 所能排列出來的全部字符串 abc, acb, bac, bca, cab 和 cba。html

 

通常思路:java

正常人的思惟是,固定第一個字符,而後依次將後面的字符串與前面的交換,那麼排列的個數就是除了第一個字符之外,其餘字符的排列個數+1。算法

也就是固定一個字符串以後,以後再將問題變小,只需求出後面子串的排列個數就能夠得出結果,固然第一時間想到的就是遞歸的算法了。數組

 

下面這張圖很清楚的給出了遞歸的過程:函數

固定第一個位置,調換後面的位置。spa

很明顯,遞歸的出口,就是隻剩一個字符的時候,遞歸的循環過程,就是從每一個子串的第二個字符開始依次與第一個字符交換,而後繼續處理子串。code

還有一個問題要注意,就是若是字符串中有重複的字符串。htm

因爲全排列就是從第一個數字起,每一個數分別與它後面的數字交換,咱們先嚐試加個這樣的判斷——若是一個數與後面的數字相同那麼這兩個數就不交換 了。例如 abb,第一個數與後面兩個數交換得 bab,bba。而後 abb 中第二個數和第三個數相同,就不用交換了。可是對 bab,第二個數和第三個數不一樣,則須要交換,獲得 bba。因爲這裏的bba和開始第一個數與第三個數交換的結果相同了,所以這個方法不行。blog

換種思惟,對abb,第一個數a與第二個數b交換獲得bab,而後考慮第一個數與第三個數交換,此時因爲第三個數等於第二個數,因此第一個數就再也不用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換能夠解決bba。此時全排列生成完畢!遞歸

 

這樣,咱們獲得在全排列中去掉重複的規則:

去重的全排列就是從第一個數字起,每一個數分別與它後面非重複出現的數字交換。

/**
     * 一、遞歸算法
     *
     * 解析:http://www.cnblogs.com/cxjchen/p/3932949.html  (感謝該文做者!)
     *
     * 對於無重複值的狀況
     *
     * 固定第一個字符,遞歸取得首位後面的各類字符串組合;
     * 再把第一個字符與後面每個字符交換,並一樣遞歸得到首位後面的字符串組合; *遞歸的出口,就是隻剩一個字符的時候,遞歸的循環過程,就是從每一個子串的第二個字符開始依次與第一個字符交換,而後繼續處理子串。
     *
     * 假若有重複值呢?
     * *因爲全排列就是從第一個數字起,每一個數分別與它後面的數字交換,咱們先嚐試加個這樣的判斷——若是一個數與後面的數字相同那麼這兩個數就不交換了。
     * 例如abb,第一個數與後面兩個數交換得bab,bba。而後abb中第二個數和第三個數相同,就不用交換了。
     * 可是對bab,第二個數和第三個數不 同,則須要交換,獲得bba。
     * 因爲這裏的bba和開始第一個數與第三個數交換的結果相同了,所以這個方法不行。
     *
     * 換種思惟,對abb,第一個數a與第二個數b交換獲得bab,而後考慮第一個數與第三個數交換,此時因爲第三個數等於第二個數,
     * 因此第一個數就再也不用與第三個數交換了。再考慮bab,它的第二個數與第三個數交換能夠解決bba。此時全排列生成完畢!
     *
     *
     * @param str
     * @return
     */

public ArrayList<String> Permutation(String str){
 
        ArrayList<String> list = new ArrayList<String>();
        if(str!=null && str.length()>0){   //只要字符串不爲空同時長度>0
            PermutationHelper(str.toCharArray(),0,list);
            Collections.sort(list);
        }
        return list;
    }
    private void PermutationHelper(char[] chars,int i,ArrayList<String> list){
        if(i == chars.length-1){
            list.add(String.valueOf(chars));
        }else{
            Set<Character> charSet = new HashSet<Character>();
            for(int j=i;j<chars.length;++j){
                if(j==i || !charSet.contains(chars[j])){
                    charSet.add(chars[j]);
                    swap(chars,i,j);
                    PermutationHelper(chars,i+1,list);
                    swap(chars,j,i);
                }
            }
        }
    }
 
    private void swap(char[] cs,int i,int j){
        char temp = cs[i];
        cs[i] = cs[j];
        cs[j] = temp;
    }
import java.util.ArrayList;
import java.util.List;
import java.util.Collections;

public class Solution {
    public ArrayList<String> Permutation(String str) {
        List<String> resultList = new ArrayList<>();
        if(str.length() == 0)
            return (ArrayList)resultList;
        //遞歸的初始值爲(str數組,空的list,初始下標0)
        fun(str.toCharArray(),resultList,0);
        Collections.sort(resultList);
        return (ArrayList)resultList;
    }
     
    private void fun(char[] ch,List<String> list,int i){
        //這是遞歸的終止條件,就是i下標已經移到char數組的末尾的時候,考慮添加這一組字符串進入結果集中
        if(i == ch.length-1){
            //判斷一下是否重複
            if(!list.contains(new String(ch))){
                list.add(new String(ch));
                return;
            }
        }else{
            //這一段就是回溯法,這裏以"abc"爲例
             
            //遞歸的思想與棧的入棧和出棧是同樣的,某一個狀態遇到return結束了以後,會回到被調用的地方繼續執行
             
            //1.第一次進到這裏是ch=['a','b','c'],list=[],i=0,我稱爲 狀態A ,即初始狀態
            //那麼j=0,swap(ch,0,0),就是['a','b','c'],進入遞歸,本身調本身,只是i爲1,交換(0,0)位置以後的狀態我稱爲 狀態B 
            //i不等於2,來到這裏,j=1,執行第一個swap(ch,1,1),這個狀態我稱爲 狀態C1 ,再進入fun函數,此時標記爲T1,i爲2,那麼這時就進入上一個if,將"abc"放進list中
            /////////////-------》此時結果集爲["abc"]
             
            //2.執行完list.add以後,遇到return,回退到T1處,接下來執行第二個swap(ch,1,1),狀態C1又恢復爲狀態B
            //恢復完以後,繼續執行for循環,此時j=2,那麼swap(ch,1,2),獲得"acb",這個狀態我稱爲C2,而後執行fun,此時標記爲T2,發現i+1=2,因此也被添加進結果集,此時return回退到T2處往下執行
            /////////////-------》此時結果集爲["abc","acb"]
            //而後執行第二個swap(ch,1,2),狀態C2迴歸狀態B,而後狀態B的for循環退出回到狀態A
             
            //             a|b|c(狀態A)
            //               |
            //               |swap(0,0)
            //               |
            //             a|b|c(狀態B)
            //             /  \
            //   swap(1,1)/    \swap(1,2)  (狀態C1和狀態C2)
            //           /      \
            //         a|b|c   a|c|b
             
            //3.回到狀態A以後,繼續for循環,j=1,即swap(ch,0,1),即"bac",這個狀態能夠再次叫作狀態A,下面的步驟同上
            /////////////-------》此時結果集爲["abc","acb","bac","bca"]
             
            //             a|b|c(狀態A)
            //               |
            //               |swap(0,1)
            //               |
            //             b|a|c(狀態B)
            //             /  \
            //   swap(1,1)/    \swap(1,2)  (狀態C1和狀態C2)
            //           /      \
            //         b|a|c   b|c|a
             
            //4.再繼續for循環,j=2,即swap(ch,0,2),即"cab",這個狀態能夠再次叫作狀態A,下面的步驟同上
            /////////////-------》此時結果集爲["abc","acb","bac","bca","cab","cba"]
             
            //             a|b|c(狀態A)
            //               |
            //               |swap(0,2)
            //               |
            //             c|b|a(狀態B)
            //             /  \
            //   swap(1,1)/    \swap(1,2)  (狀態C1和狀態C2)
            //           /      \
            //         c|b|a   c|a|b
             
            //5.最後退出for循環,結束。
             
            for(int j=i;j<ch.length;j++){
                swap(ch,i,j);
                fun(ch,list,i+1);
                swap(ch,i,j);
            }
        }
    }
     
    //交換數組的兩個下標的元素
    private void swap(char[] str, int i, int j) {
            if (i != j) {
                char t = str[i];
                str[i] = str[j];
                str[j] = t;
            }
        }
    }
相關文章
相關標籤/搜索