文章目录
说明:
原文:July 算法习题 - 字符串4(全排列和全组合)
https://segmentfault.com/a/1190000002710424
【july的算法讲解及其推荐啊,非常透彻!】
字符串的排列组合题型集合
字符串的全排列
题目
设计一个算法,输出一个字符串字符的全排列。
比如,String = “abc”
输出是"abc",“bac”,“cab”,“bca”,“cba”,“acb”
算法思想
从集合依次选出每一个元素,作为排列的第一个元素,然后对剩余的元素进行全排列,如此递归处理;
比如:首先我要打印abc的全排列,就是第一步把a 和bc交换(得到bac,cab),这需要一个for循环,循环里面有一个swap,交换之后就相当于不管第一步了,进入下一步递归,所以跟一个递归函数, 完成递归之后把交换的换回来,变成原来的字串
递归方法1(July 方法):
abc 为例子:
- 固定a, 求后面bc的全排列: abc, acb。 求完后,a 和 b交换; 得到bac,开始第二轮
- 固定b, 求后面ac的全排列: bac, bca。 求完后,b 和 c交换; 得到cab,开始第三轮
- 固定c, 求后面ba的全排列: cab, cba
即递归树:
str: a b c
ab ac ba bc ca cb
result: abc acb bac bca cab cba
public static void Permutation(char[] s, int from, int to) {
if(to<=1)
return;
if(from == to){
System.out.println(s);
}
else{
for(int i=from;i<=to;i++){
swap(s,i,from);
Permutation(s,from+1,to);
swap(s,from,i);
}
}
}
public static void swap(char[] s, int i, int j) {
char temp = s[i];
s[i] = s[j];
s[j] = temp;
}
递归方法2:
与上面算法区别:
本算法需要一个额外的存储空间存放结果(buffer),固定第一个位置是哪个元素的时候,是通过一个循环,然后看原始字符串上,每一个位置是什么元素。July的做法没有结果的buffer,都是在一个字符串上进行的操作。第一个swap的作用就是,依次拿起始字符和后面的每一个字符交换,这样就能遍历第一个位置上的所有可能字符
n个数的全排列,一共有n!种情况. (n个位置,第一个位置有n种,当第一个位置固定下来之后,第二个位置有n-1种情况…)
全排列的过程:
- 选择第一个字符
- 获得第一个字符固定下来之后的所有的全排列
- 选择第二个字符
- 获得第一+ 二个字符固定下来之后的所有的全排列
从这个过程可见,这是一个递归的过程。
还有一点需要注意是:
之前递归过程选择的字符,下一次不能再被选: 第一个位置选了a, 其他位置就不能选a了
解决方法是1. 扫描之前选择的字符 或者 2.创建一个与字符串等长的boolean数组,标记该位置对于的字符是否已经选择。若选择,则标记true; 若未选择,则标记false.
个人认为这个算法不如第一个递归方法,因为需要额外的空间;但是二者的时间复杂度是相同的,都是O(n!)。
public class Permutation {
public static void permute(String str){
int length = str.length();
boolean[] used = new boolean[length];
StringBuffer output = new StringBuffer(length);