题目描述
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。


本题的常规解法还是以递归解法为主,学有余力的可以用巧劲解题。可以参考题解的讨论中,五头镜子这位大佬的总结。
递归思路:求整个字符串的排序,将递归设计成两步,步骤如下,第一步求所有出现在第一个位置的字符,也就是将第一个字符和后面所有字符交换,第二步固定第一个字符,求后面所有字符的排序。进入下一次递归调用时,则是将所有的字符和第二个字符交换,然后固定之后,求后面所有字符的排序。
  以abc为例,首先是abc,bac,cba三种,再求abc固定a后的排序方式,此时调用递归,则原问题的子问题变成了求bc的排序方式。同理bac和cba的子问题变成了求ac,ba的排序方式。
  注意输出的排序方式是字典序,因此需要排序。另外,java中的StringBuilder和StringBuffer在操作字符串时,效率比String高,推荐使用。还有一个点要注意的是,需要去重。去重也有好几种处理方法,1.在遍历的过程中应该写进行去重的比较判断的逻辑。2.在java中,使用实现了set接口的工具类即可,如TreeSet,HashSet,使用这些结构在添加的时候就自动去重了。在不同类的选择上,就可以出现多种写法了。
写法1:

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Collections;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        StringBuilder buffer = new StringBuilder(str); //使用StringBuilder       

        ArrayList<String> list1 = PermutationHelp(buffer);

        HashSet<String> set = new HashSet<>(list1);//构造包含list所有元素的HashSet
        ArrayList<String> list2 = new ArrayList<String>(set);//再装到数组中
        Collections.sort(list2);//进行字典序排序
        return list2;
    }

    public ArrayList<String> PermutationHelp(StringBuilder str){
        ArrayList<String> result = new ArrayList<>();//用来装所有结果的数组
        if (str.length()==1){//递归终止条件
            result.add(str.toString());
        }else {
            for (int i=0;i<str.length();i++){
                //下面三句是将字符串进行交换
                char temp = str.charAt(i);
                str.setCharAt(i,str.charAt(0));
                str.setCharAt(0,temp);
                //交换后,除去刚刚交换的字符串,剩余的字符串继续调用递归
                StringBuilder buf = new StringBuilder(str.substring(1));
                ArrayList<String> arrayList = PermutationHelp(buf);
                //到这里说明某一次交换的递归完成,此时需要将结果添加
                for (int j=0;j<arrayList.size();j++){
                //添加字符串[start,end)中的字符串
                    result.add(str.substring(0,1)+arrayList.get(j));
                }
                //temp=str.charAt(0);
                //str.setCharAt(0,str.charAt(i));
                //str.setCharAt(i,temp);
//上面三句是将换出的字符再换回去,但实际可以省略
//不换回去,从整体上来看,顺序乱了,但是遍历时依旧会遍历所有情况,在纸上画一画很清楚。
//这3句可以省略,因为最终使用了Collections的排序方法,使得即使不换回去,排序过后依旧字典序
//添加这三句代码,就有点回溯的味道了。
            }
        }
        return result;
    }
}
//result.add(str.substring(0,1)+arrayList.get(j));不容易理解,如abc定住a后,子问题是bc
//递归的返回值,定b后,返回c,则str取b,arrayList取c,合并成bc添加到result,
//递归的返回值,定c后,返回b,则str取c,arrayList取b,合并成cb添加到result,
//arrayList是拿到的就是result
//再向上返回时,str变成a,则arrayList是bc和cb的集合,所以需要一个for循环。拼接成abc和acb

写法2:由于TreeSet底层红黑树实现了排序,所以用TreeSet就不用调用排序方法。对比方法1,只需要修改Permutation方法中的几行,剩下的PermutationHelp方法完全一样

import java.util.ArrayList;
import java.util.TreeSet;
public class Solution {
    public ArrayList<String> Permutation(String str) {
        StringBuilder buffer = new StringBuilder(str); //使用StringBuilder             
        ArrayList<String> list1 = PermutationHelp(buffer);     
        TreeSet<String> set = new TreeSet<>(list1);//构造一个包含list所有元素的TreeSet
        ArrayList<String> list2 = new ArrayList<String>(set);//再装到数组中       
        return list2;
    }

    public ArrayList<String> PermutationHelp(StringBuilder str){
        ArrayList<String> result = new ArrayList<>();
        if (str.length()==1){
            result.add(str.toString());
        }else {
            for (int i=0;i<str.length();i++){
                char temp = str.charAt(i);
                str.setCharAt(i,str.charAt(0));
                str.setCharAt(0,temp);
                StringBuilder buf = new StringBuilder(str.substring(1));
                ArrayList<String> arrayList = PermutationHelp(buf);
                for (int j=0;j<arrayList.size();j++){
                    result.add(str.substring(0,1)+arrayList.get(j));
                }
            }
        }
        return result;
    }
}

写法3:在遍历的过程中,进行重复的判断。

import java.util.ArrayList;
import java.util.Collections;
public class Solution {
     public ArrayList<String> Permutation(String str) {
        StringBuilder strBuilder = new StringBuilder(str);
        ArrayList<String> result = PermutationHelp(strBuilder);
        Collections.sort(result);
        return result;
    }

    public ArrayList<String> PermutationHelp(StringBuilder str){
         ArrayList<String> result = new  ArrayList<String>();
        if(str.length() == 1)
            result.add(str.toString());
        else{
            for(int i = 0; i < str.length(); i++){
                if(i== 0  || str.charAt(i) != str.charAt(0)){//这里判断
                    char temp = str.charAt(i);
                    str.setCharAt(i, str.charAt(0));
                    str.setCharAt(0, temp);
                    StringBuilder buf = new StringBuilder(str.substring(1));
                    ArrayList<String> arrayList = PermutationHelp(buf);
                    for(int j =0; j < arrayList.size(); j++)
                        result.add(str.substring(0,1)+arrayList.get(j));  
                    temp = str.charAt(0);
                    str.setCharAt(0, str.charAt(i));
                    str.setCharAt(i, temp);
                }               
            }
        }
        return result;
    }
}
//很神奇的是,如果不交换回去,就不用调用集合类的排序方法。下面的写法也可以跑起来
//这个倒是没弄清楚。不知道是测试用例不全还是什么原因。
import java.util.ArrayList;
public class Solution {
     public ArrayList<String> Permutation(String str) {
        StringBuilder strBuilder = new StringBuilder(str);
        ArrayList<String> result = PermutationHelp(strBuilder);
        return result;
    }

    public ArrayList<String> PermutationHelp(StringBuilder str){
         ArrayList<String> result = new  ArrayList<String>();
        if(str.length() == 1)
            result.add(str.toString());
        else{
            for(int i = 0; i < str.length(); i++){
                if(i== 0  || str.charAt(i) != str.charAt(0)){
                    char temp = str.charAt(i);
                    str.setCharAt(i, str.charAt(0));
                    str.setCharAt(0, temp);
                    StringBuilder buf = new StringBuilder(str.substring(1));
                    ArrayList<String> arrayList = PermutationHelp(buf);
                    for(int j =0; j < arrayList.size(); j++)
                        result.add(str.substring(0,1)+arrayList.get(j));                  
                }                
            }
        }
        return result;
    }
}