题目
设计一个算法,输出一个字符串字符的全排列。 比如,String = “ABC” 输出是
ABC
ACB
BAC
BCA
CBA
CAB
思路
可能我们的第一直觉是,这就是一个选择问题,第一个字符3选1,第二个字符2选1,第三个字符没得选,三层循环可快速暴力求解。可以用嵌套循环来生成新的字符串。
但是这个字符串的长度是不定的,我们没有一种语法可以指定循环的层数。
如果层数根据运行情况来定,唯有递归。
使用递归必须抽象出一个局面x,当下处理一部分使得局面简化为y,递归求解局面y,以此类推,直到问题足够简单可显然求解,然后层层返回。
抽象递归时,我们可以想象我有一个下属,下属又有一个下属,所有这些人都能解决同样的问题。最高层领导只需将局面简化一点点,然后委派下属去处理这个简化后的类似的局面即可,然后就是等结果。
在此题中,我们可以这样考虑:
目标:输出全排列(字符数组)
我来搞定第一个字符
剩下的事,下级来处理剩下的部分
处理到最后一个字符时,就可以输出这个重新排列过的数组了
但很快你会发现,搞定第一个字符有很多种选择,上面的推理需要改写成:
目标:输出全排列(字符数组)
我来搞定第一个字符,这件事我会重复N次,每次选字符串中的一个
一旦选定(把某个字符挪到首位)
剩下的事,下级来处理剩下的部分
因为下次我要再选一个,所以先恢复到挪位之前
那么什么时候输出呢?
这个函数每调用一次,都是上级排好了前面的字符,我来处理后续字符,如果我处理的是最后一个字符,因为上级已经排好前面的,我整体输出全部字符即可。
伪码
// data:上级给任务时字符数组,index:上级已经排好了0~index-1的字符,要我处理index~length-1这段
f(char[] data,int index)
// 现在我试着从index开始找一个字符来作为首位,我要尝试length-index次
for(int i=index;i<length;i++)
swap(data,i,index);// 把i位置的字符交换到index这里来,我的工作就完成了
// 下面请直接下级来接招
f(data,index+1);
// 下级排好了,我即将选定下一个字符作为首位,必须先恢复一下
swap(data,i,index);
// 如果上级们已经处理好全部字符,我就输出
if(index==length)
println(data);
Java代码
package org.lanqiao.algo.recursion;
/**
* 输出字符串所有字符的全排列
* */
public class StrPermutation {
static int n;
public static void main(String[] args) {
String s = "ABCDE";
permutation(s.toCharArray(), 0);
System.out.println("---" + n + "---"); // n=长度的阶乘,可以用这个数字验证算法的正确性
}
//index代表0~index-1都已确认排列,[index,n-1]待排列
private static void permutation(char[] arr, int index) {
//至于什么时候输出,要考虑清楚
//考虑:只有所有位置上的字符都确认即index到达末尾,才算是排好一种情况
if (index == arr.length) {
n++;
System.out.println(String.valueOf(arr));
}
//现在index之前的字符都已就位,把之后的每个字符都交换到index这个位置
for (int k = index; k < arr.length; k++) {
//尝试交换
swap(arr, index, k);
//交换之后index这个位置就定好了,接下来的事就是递归去解决index+1位置开始的全排列就好了
permutation(arr, index + 1);
// 前面我们尝试交换,把全排列求出来了,交换时只尝试了一个字符,因此for循环继续之前要换回来,继续尝试交换下一个字符
swap(arr, index, k);
}
}
private static void swap(char[] arr, int index, int k) {
if(k==index)
return; // 不必交换了吧
char tmp = arr[k];
arr[k] = arr[index];
arr[index] = tmp;
}
}
小结
最终代码其实比较简短,这是递归的特点:代码简单,思路绕弯。考验抽象思维和想象力。
使用递归,必须找到问题分解后子问题的相似性,换句话说,你必须找到某种分解结构,按照分解链路可以将问题简化直至可直接得出结论。