字符串全排列的两个例子:
1.abc的全排列有:
abc
acb
bac
bca
cba
cab
2.aab的全排列有:
aab
aba
baa
思路:使用回溯法。对于abc,a和a交换仍是abc;a和b交换可得bac;a和c交换可得cba,至此第一个字母的遍历结束。从上述结果中进行第二个字母的遍历:对于abc,b和b交换仍是abc,b和c交换可得acb;对于bac,a和a交换仍是bac,a和c交换可得bca;对于cba,b和b交换仍是cba,b和a交换可得cab。至此我们得到abc的全部6种排列。归纳上述方法,即:对于长度为n的字符串,需要对其第1至第n-1个字符进行遍历。所谓“遍历”是指将该位置的字符与其本身以及其后的所有字符均交换一遍,得出下一轮“遍历”的若干分支。第一轮遍历会获得n个分支,第二轮在此基础上获得n*(n-1)个分支,第三轮为n*(n-1)*(n-2)个分支,……,第n-1轮为n*(n-1)*(n-2)*……*2 = n!个分支,正符合全排列公式
上述分析只针对无重复字符的字符串。对于有重复字符的字符串上述算法不会得出正确结果,以abb为例:第一轮的一个分支是bab,另一个分支是bba。第二轮中bba的一个分支是bab,这与第一轮得到的bab重复,因此出错
我们只需对上述算法做一些改进即可适用于有重复字符的字符串,具体为:在某一轮“遍历”中,若当前准备被交换的字符在该轮已被交换过,则跳过该次交换
import java.io.BufferedInputStream;
import java.util.Scanner;
public class pangguo {
public static void main(String args[]) {
Scanner cin = new Scanner(new BufferedInputStream(System.in)); //输入
String str = cin.next();
char[] ch = str.toCharArray();
new pangguo().process(ch,ch.length,0);
}
public void process(char s[],int n,int k) {
if(k == n-1) //递归的出口,若遍历至最后一个字符,则无法再交换,打印此分支
System.out.println(s);
else{
for(int i = k; i < n; i++){ //交换包括其本身及之后的所有字符
int exist = 0; //标记当前计划交换的字符之前是否出现过,0为未出现
for(int j = k; j < i; j++) //从k扫描至i-1
if(s[j] == s[i]){
exist = 1;
break;
}
if(exist == 0){ //若未出现过,则进行交换
swap(s,k, i);
process(s, n,k+1); //交换后进行递归,进行下一轮“遍历”
swap(s,k, i); //必须交换回来,为了本轮其他分支使用
}
}
}
}
public void swap(char[] s,int k, int i) {
{
char temp;
temp = s[k];
s[k] = s[i];
s[i] = temp;
}
}
}