一、问题
描述
给定一个字符串,求它的全排列。
示例输入
abc
示例输出
abc
acb
bac
bca
cba
cab
二、分析
在高中数学中,求所有的排列,我们往往会按每个开头的有几种进行列举。比如abc,我们会分析所有a开头的有几个,b开头的有几个,依次列举。
在a开头的里面,再进行继续列举,a开头前提下,剩下的b开头有几个,c开头有几个。
可以看见这个过程和分治思想非常像。分治思想的解决方法是使用递归。
递归关心两个:
- 1、递归公式是什么?
- 2、什么时候结束?
为了更好的描述这两个问题,需要将问题进行数字化,用公式字母表示。
问题是求字符串的所有全排列,因为这个涉及字符串里面每个字母,所以假设字符串用s={s1,s2,s3,s4…sn}表示。全排列用Perm(s, si)表示去掉si之后的全排列。
这时候看两个问题: - 1、递归公式是什么?
从上面我们解数学题的思考就知道所有的全排列只要找出每个字母开头的全排列。
即:
s1Perm(s, s1) + s2Perm(s, s2) … + snPerm(s, sn) - 2、什么时候结束 ?
当n=1,即只有一个元素的时候,那么全排列就是本身一个。
注意:这里需要判断字符是否有相同,即如果s1 ~ sn有相同的,那就应该跳过计算。所以需要记录下已经开头过的,可以用Set进行存储。
三、代码
import java.util.HashSet;
import java.util.Scanner;
import java.util.Set;
public class Main {
public static void main(String []args) {
Scanner in = new Scanner(System.in);
String str = in.nextLine();
char[] list = str.toCharArray();
perm(list, 0, list.length - 1);
}
//对list[s]到list[e]进行排列
public static void perm(char[] list, int s, int e) {
//结束递归
if (s == e) {
System.out.println(String.valueOf(list));
return;
}
//开始递归
Set<Character> characters = new HashSet<>(); //存储已经开头过的char
for(int i = s; i <= e; i++ ) {
if(characters.contains(list[i])) {
continue;
}
characters.add(list[i]);
swap(list, s, i);
perm(list, s + 1, e);
swap(list, s, i);
}
}
private static void swap(char[] list, int i, int j) {
char temp = list[i];
list[i] = list[j];
list[j] = temp;
}
}