算法思维(递归)训练:输出字符串字符的全排列
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; } }
小结
最终代码其实比较简短,这是递归的特点:代码简单,思路绕弯。考验抽象思维和想象力。使用递归,必须找到问题分解后子问题的相似性,换句话说,你必须找到某种分解结构,按照分解链路可以将问题简化直至可直接得出结论。
相关文章推荐
- 算法思维(递归)训练:输出字符串长度为M的子序列
- 递归解决输出一个字符串的全排列问题(缺陷:没有考虑字符串中字符重复的问题)
- 算法十二:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b
- 算法习题17:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b
- 递归复习,递归输出字符串的全排列
- Swift - 排列组合之全排列 (输入一个字符串,输出该字符串包含的字符的所有组合)
- 普通递归算法:输出n个数的所有全排列
- 算法训练 6-2递归求二进制表示位数 ;求完数;求指数;字符串变换
- 递归问题之一:给定一字符串,输出字符串中所有字符子串的各种组合形式,长度为一个字符到字符串的长度,忽略排序
- java一个算法题:输出一个字符串中出现次数最多的字符,以及次数
- java一个算法题:输出一个字符串中出现次数最多的字符,以及次数
- 算法 - 输出一个字符串的全排列(C++)
- [算法] 输出 字符串的全部子组合 [dfs - 递归神技]
- [算法] 字符串的全排列 [dfs - 递归神技]
- [算法] 字符串的全排列 [dfs - 递归神技]
- 输出字符串的所有全排列(递归法和非递归,非递归采用组合数学的字典序)
- 对输入的字符串按字典顺序输出所有的全排列,字符串可以由重复字符
- [算法] 输出 字符串的全部子组合 [dfs - 递归神技]
- 每日一道算法题:在一个字符串中找到第一个只出现一次的字符。如输入abaccdeff,则输出b。
- 字符串全排列问题(递归解决有重复字符问题)