题目
输入一个字符串,按字典序打印出该字符串中字符的所有排列。例如输入字符串 abc,则打印出由字符 a, b, c 所能排列出来的所有字符串 abc, acb, bac, bca, cab 和 cba。
示例:
输入:
abc
输出:
abc
acb
bac
bca
cab
cba
思路
- 有要求排序,则需要将字符串所有字符取出生成等长的char数组并进行排序,再根据排序后的结果选择字符进行组合;
- 选择字符的过程可以用递归实现,使用for循环进行递归调用也可以满足字典序进行组合,那么这个过程中需要有一个boolean数组来标志该字符是否已被使用;
- 递归终止条件设置为当前组合的字符串长度等于char数组的长度;
- 递归体中使用一个for循环,从排好序的char数组的第一位开始进行遍历与组合可以保证字典序。首先应该查询该位是否使用过,已使用则跳过该字符去遍历下一个字符,未使用则更新使用状态为使用(true),然后将该字符加入组合的串中,接着递归调用。待递归返回后,从串中删除该字符,将使用状态更新为未使用(false),达到回溯。
代码
private ArrayList<String> ret = new ArrayList<>();
public ArrayList<String> solute(String str){
if (str.length()==0) {
return ret;
}
char[] chars = str.toCharArray();
Arrays.sort(chars);
back(chars,new boolean[chars.length],new StringBuilder());
return ret;
}
//hasUsed存储对应位置的字符使用情况,s存储当前组合方案
private void back(char[] chars,boolean[] hasUsed,StringBuilder s){
//完成组合时返回
if(chars.length==s.length()){
//这里需要创建一个新的String对象,
//也可以使用s.toString()
ret.add(new String(s));
return;
}
//for循环保证按照字典序遍历组合
for (int i=0;i<chars.length;i++) {
//当前字符已被使用则跳过
if(hasUsed[i])
continue;
//保证位置靠前的重复字符被优先使用
if(i!=0&&chars[i]==chars[i-1]&&!hasUsed[i-1])
continue;
hasUsed[i] = true;
s.append(chars[i]);
//递归调用,此时hasUsed和s都是被更新了的
back(chars,hasUsed,s);
//递归调用返回后,进行回溯
s.deleteCharAt(s.length()-1);
hasUsed[i] = false;
}
}