字符串的全排列详解,递归+非递归
2016-08-06 20:04
253 查看
问题:
输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba1.全排列的递归实现
思路
首先,我们固定第一个字符a,求后面两个字符bc的排列当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列
现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列
既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了
#include"stdafx.h" #include"iostream" #include <string.h> using namespace std; int count; //统计全排列的个数 void swap(char *str, int a, int b) { char temp; temp = str[a]; str[a] = str; str[b] = temp; } void permutation_process(char *str, int begin, int end) { int k; if (begin == end - 1) { cout<<"第"<<count++<<"个全排列"<<str<<endl; <span style="white-space:pre"> </span>}else { for (k = begin; k < end; k ++) { swap(str, k, begin); permutation_process(str, begin + 1, end); swap(str, k, begin); } } } int _tmain(int argc, _TCHAR* argv[]) { <span style="white-space:pre"> </span>char str[7]; <span style="white-space:pre"> </span>int i, len; <span style="white-space:pre"> </span>cin>>str; <span style="white-space:pre"> </span>count = 1; len = strlen(str); permutation_process(str, 0, len); return 0; }下面这张图给出了递归的过程
如果字符串中有重复字符的话,上面的那个方法肯定不会符合要求的,因此现在要想办法来去掉重复的数列。
[b]2、去掉重复的全排列的递归实现
由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
这样,我们得到在全排列中去掉重复的规则:
去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。
#incl 4000 ude "stdafx.h" #include"iostream" #include <string.h> using namespace std; int count; //统计全排列的个数 void swap(char *str, int a, int b) { char temp; temp = str[a]; str[a] = str[b]; str[b] = temp; } int is_swap(char *str, int begin, int k) { int i, flag; for (i = begin, flag = 1; i < k; i ++) { if (str[i] == str[k]) { flag = 0; break; } } return flag; } void permutation_process(char *str, int begin, int end) { int k; if (begin == end - 1) { cout<<"第"<<count++<<"个全排列"<<str<<endl; }else { <span style="white-space:pre"> </span> for (k = begin; k < end; k ++) { <span style="white-space:pre"> </span> if(is_swap(str, begin, k) ){ <span style="white-space:pre"> </span>swap(str, k, begin); <span style="white-space:pre"> </span>permutation_process(str, begin + 1, end); <span style="white-space:pre"> </span>swap(str, k, begin); } } <span style="white-space:pre"> </span> } } int _tmain(int argc, _TCHAR* argv[]) { int i, len; cin>>str; count = 1; len = strlen(str); permutation_process(str, 0, len); return 0; }
3、全排列非递归实现
思路
要考虑全排列的非递归实现,先来考虑如何计算字符串的下一个排列。如"1234"的下一个排列就是"1243"。只要对字符串反复求出下一个排列,全排列的也就迎刃而解了。如何计算字符串的下一个排列了?来考虑"926520"这个字符串,我们从后向前找第一双相邻的递增数字,"20"、"52"都是非递增的,"26 "即满足要求,称前一个数字2为替换数,替换数的下标称为替换点,再从后面找一个比替换数大的最小数(这个数必然存在),0、2都不行,5可以,将5和2交换得到"956220",然后再将替换点后的字符串"6220"颠倒即得到"950226"。
如果达到这个数的最大,比如1234-4321,这个时候就结束整个循环。
如果输入是一个非最小数,如1324,则将它转换为最小数,如1234,再进行排序。排序算法用快排,可以自己写一个,如果快排不会的话,就先看会再来接着看,或者自己想一个靠谱的算法,也可以直接用VC库中的qsort(s , n , sizeof(s[0]) , cmp);各参数是什么意思就自己在下面多花点时间吧。
OK,下面看代码分析
//全排列非递归 #include"stdafx.h" #include<iostream> #include<algorithm> #include<cstring> using namespace std; #include<assert.h> //反转区间 void Reverse(char* pBegin , char* pEnd) { while(pBegin < pEnd) swap(*pBegin++ , *pEnd--); } //下一个排列 bool Next_permutation(char a[]) { assert(a); char *p , *q , *pFind; char *pEnd = a + strlen(a) - 1; if(a == pEnd) return false; p = pEnd; while(p != a) { q = p; p--; if(*p < *q) //找降序的相邻2数,前一个数即替换数 { //从后向前找比替换点大的第一个数 pFind = pEnd; while(*pFind < *p) --pFind; swap(*p , *pFind); //替换点后的数全部反转 Reverse(q , pEnd); return true; } } Reverse(a , pEnd); //如果没有下一个排列,全部反转后返回false return false; } int cmp(const void *a,const void *b) { return int(*(char *)a - *(char *)b); } int main(void) { char str[] = "bac"; int num = 1; qsort(str , strlen(str),sizeof(char),cmp); do { printf("第%d个排列\t%s\n",num++,str); }while(Next_permutation(str)); return 0; }
相关文章推荐
- 输出字符串的所有全排列(递归法和非递归,非递归采用组合数学的字典序)
- 全排列(递归与非递归)
- 全排列的递归和非递归的实现
- 全排列(含递归和非递归的解法)
- 递归复习,递归输出字符串的全排列
- 全排列(含递归和非递归的解法)
- 将一个整数N转换成字符串!(递归和非递归、及——进制转化)
- leetcode 46. Permutations-全排列|回溯|递归|非递归
- 字符串逆置(递归和非递归)
- 字符串的全排列 递归
- [算法] 字符串的全排列 [dfs - 递归神技]
- 全排列(含递归和非递归的解法)(转)
- 全排列(含递归和非递归的解法)
- 求字符串的全排列(递归方法)
- 字符串拷贝函数递归与非递归的C语言实现
- 全排列(含递归和非递归的解法)
- 递归--字符串的全排列
- 字符串全排列的递归和非递归实现
- 递归应用——字符串全排列,字符串组合
- 二叉树的遍历详解(前序中序后序层次-递归和非递归)