【题目描述】
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串abc,则打印出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba。输入描述:
输入一个字符串,长度不超过9(可能有字符重复),字符只包括大小写字母。
2023.1.28更新方法
回溯方法+set去重
public ArrayList<String> Permutation(String str) {
Set<String> list = new HashSet<>();
boolean[] visited = new boolean[str.length()];
recursion(str,"",visited,list);
ArrayList<String> res = new ArrayList<>(list);
return res;
}
public void recursion(String str,String current,boolean[] visited,Set<String> res){
if(str.length()==current.length()){
res.add(current);
return;
}
for(int i = 0;i<str.length();i++)
{
if(!visited[i]){
current+=str.charAt(i);
visited[i] = true;
recursion(str,current,visited,res);
current = current.substring(0,current.length()-1);
visited[i] = false;
}
}
}
【解题思路】
把字符串分为两部分:一部分是字符串的第一个字符,另一部分是第一个字符以后的所有字符。
- 第一步是求所有可能出现在第一个位置的字符,即把第一个字符和后面所有字符交换。(for循环、交换操作)
- 第二步是固定住第一个字符,求后面所有字符的排列。(递归)如何求剩余字符的排列:将剩余字符的第一个字符依次和后面的字符进行交换,
- 而“求后面所有字符的排列”即可按照上面的思路递归进行。
- 递归出口:求剩余字符的排列时只剩下一个字符
实现借助一个char[],通过交换元素得到不同的排列,在递归返回时将其装入ArrayList。
如下图所示,有两点需要注意:
- 在每个分支进行完后,要将交换过的元素复位,从而不会影响其他分支。
- 因为有“Swap A with A”这样的操作,并且题目指出可能有字符重复,所以分支末尾可能有重复的序列,在加入ArrayList要进行去重判断,不重复再加入。
以 abc为例,图解过程如下:
代码如下:
public static ArrayList<String> Permutation(String str) {
ArrayList<String> list = new ArrayList<>();
if(str == null)
return list;
PermutationHelper(str.toCharArray(), list, 0);
Collections.sort(list);//对所得的list集合进行排序
return list;
}
public static void PermutationHelper(char[] ch, ArrayList res, int i){
if(i == ch.length){
String str = String.valueOf(ch);
if(!res.contains(str)){
res.add(str);
}
}
for(int j = i; j < ch.length;j++){
swap(ch,i,j);//交换数组第i个字符和第j个字符
PermutationHelper(ch,res,i+1);
swap(ch,i,j);//再次交换数组第i个字符和第j个字符,保证回到此次for循环前字符数组的状态,不影响字符数组进行下一次for循环
}
}
public static void swap(char[] ch, int i ,int j) {
char tmp = ch[i];
ch[i] = ch[j];
ch[j] = tmp;
}
基于回溯思想的递归算法:
* 1. 找出所有可能出现在字符串第一个位置的字符所得的字符串
* 2. 对于步骤1中可能出现的每个字符串:固定第一个字符,求剩余字符的排列;求剩余字符的排列时跟原问题一样
*
* 如何求剩余字符的排列:将剩余字符的第一个字符依次和后面的字符进行交换
* 递归出口:求剩余字符的排列时只剩下一个字符
*
* 以 a|b|c 为例,此时list为空,由于字符串不为空且长度不为0,进入到PermutationHelper()函数中
*
* 此时 i=0
* j从0开始到2:
*
* j等于0 时:
* 交换chars[0]和chars[0]后数组变成 a|b|c
* 进入到PermutationHelper(chars,1,list)函数中:
* 此时 i=1
* j从1开始到2:
* j等于1 时:
* 交换chars[1]和chars[1]后数组变成 a|b|c
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串abc,故将“abc”存入list中并返回,此时list={"abc"}
* 交换chars[1]和chars[1]后数组变成 a|b|c
* j等于2 时:
* 交换chars[1]和chars[2]后数组变成 a|c|b
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串cba,故将“acb”存入list中并返回,此时list={"abc","acb"}
* 交换chars[1]和chars[2]后数组变成 a|b|c
* 交换chars[0]和char[0]后数组变成 a|b|c
*
* j等于1 时:
* 交换chars[0]和chars[1]后数组变成 b|a|c
* 进入到PermutationHelper(chars,1,list)函数中:
* 此时 i=1
* j从1开始到2:
* j等于1 时:
* 交换chars[1]和char[1]后数组变成 b|a|c
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串bac,故将“bac”存入list中并返回,此时list={"abc","acb","bac"}
* 交换chars[1]和chars[1]后数组变成 b|a|c
* j等于2 时:
* 交换chars[1]和chars[2]后数组变成 b|c|a
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串bca,故将“bca”存入list中并返回,此时list={"abc","acb","bac","bca"}
* 交换chars[1]和chars[2]后数组变成 b|a|c
* 交换chars[0]和chars[1]后数组变成 a|b|c
*
* j等于2 时:
* 交换chars[0]和chars[2]后数组变成 c|b|a
* 进入到PermutationHelper(chars,1,list)函数中:
* 此时 i=1
* j从1开始到2:
* j等于1 时:
* 交换chars[1]和char[1]后数组变成 c|b|a
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串cba,故将“cba”存入list中并返回,此时list={"abc","acb","bac","bca","cba"}
* 交换chars[1]和chars[1]后数组变成 c|b|a
* j等于2 时:
* 交换chars[1]和chars[2]后数组变成 c|a|b
* 进入到PermutationHelper(chars,2,list)函数中:
* 此时 i=2
* 由于此时i=2=数组长度-1,且list中不包含字符串cab,故将“cab”存入list中并返回,此时list={"abc","acb","bac","bca","cba","cab"}
* 交换chars[1]和chars[2]后数组变成 c|b|a
* 交换chars[0]和chars[2]后数组变成 a|b|c
*
* 到此,for循环结束,此时list={"abc","acb","bac","bca","cba","cab"},PermutationHelper()函数调用结束
* 利用Collections.sort()函数对list集合进行升序排序后得list={"abc","acb","bac","bca","cab","cba"}