您的位置:首页 > 其它

算法思维(递归)训练:输出字符串字符的全排列

2017-11-24 22:23 323 查看

题目

设计一个算法,输出一个字符串字符的全排列。 比如,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;
}
}


小结

最终代码其实比较简短,这是递归的特点:代码简单,思路绕弯。考验抽象思维和想象力。

使用递归,必须找到问题分解后子问题的相似性,换句话说,你必须找到某种分解结构,按照分解链路可以将问题简化直至可直接得出结论。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  算法 递归
相关文章推荐