当前位置:   article > 正文

字符串的全排列详解,递归+非递归_字符串abc 排序

字符串abc 排序

问题:

输入一个字符串,打印出该字符串中字符的所有排列。例如输入字符串abc,则输出由字符a,b,c所能排列出来的所有字符串abc,acb,bac,bca,cab和cba

1.全排列的递归实现

思路

  1. 首先,我们固定第一个字符a,求后面两个字符bc的排列
  2. 当两个字符bc排列求好之后,我们把第一个字符a和后面的b交换,得到bac,接着我们固定第一个字符b,求后面两个字符ac的排列
  3. 现在是把c放在第一个位置的时候了,但是记住前面我们已经把原先的第一个字符a和后面的b做了交换,为了保证这次c仍是和原先处在第一个位置的a交换,我们在拿c和第一个字符交换之前,先要把b和a交换回来。在交换b和a之后,再拿c和处于第一位置的a进行交换,得到cba。我们再次固定第一个字符c,求后面两个字符b、a的排列
  4. 既然我们已经知道怎么求三个字符的排列,那么固定第一个字符之后求后面两个字符的排列,就是典型的递归思路了

  1. #include"stdafx.h"
  2. #include"iostream"
  3. #include <string.h>
  4. using namespace std;
  5. int count; //统计全排列的个数
  6. void swap(char *str, int a, int b)
  7. {
  8. char temp;
  9. temp = str[a];
  10. str[a] = str[b];
  11. str[b] = temp;
  12. }
  13. void permutation_process(char *str, int begin, int end) {
  14. int k;
  15. if (begin == end - 1) {
  16. cout<<"第"<<count++<<"个全排列"<<str<<endl;
  17. <span style="white-space:pre"> </span>}else {
  18. for (k = begin; k < end; k ++) {
  19. swap(str, k, begin);
  20. permutation_process(str, begin + 1, end);
  21. swap(str, k, begin);
  22. }
  23. }
  24. }
  25. int _tmain(int argc, _TCHAR* argv[])
  26. {
  27. <span style="white-space:pre"> </span>char str[7];
  28. <span style="white-space:pre"> </span>int i, len;
  29. <span style="white-space:pre"> </span>cin>>str;
  30. <span style="white-space:pre"> </span>count = 1;
  31. len = strlen(str);
  32. permutation_process(str, 0, len);
  33. return 0;
  34. }
下面这张图给出了递归的过程


如果字符串中有重复字符的话,上面的那个方法肯定不会符合要求的,因此现在要想办法来去掉重复的数列。

2、去掉重复的全排列的递归实现

由于全排列就是从第一个数字起每个数分别与它后面的数字交换。我们先尝试加个这样的判断——如果一个数与后面的数字相同那么这两个数就不交换了。例如abb,第一个数与后面两个数交换得bab,bba。然后abb中第二个数和第三个数相同,就不用交换了。但是对bab,第二个数和第三个数不同,则需要交换,得到bba。由于这里的bba和开始第一个数与第三个数交换的结果相同了,因此这个方法不行。
换种思维,对abb,第一个数a与第二个数b交换得到bab,然后考虑第一个数与第三个数交换,此时由于第三个数等于第二个数,所以第一个数就不再用与第三个数交换了。再考虑bab,它的第二个数与第三个数交换可以解决bba。此时全排列生成完毕!
这样,我们得到在全排列中去掉重复的规则:
去重的全排列就是从第一个数字起,每个数分别与它后面非重复出现的数字交换。
  1. #include "stdafx.h"
  2. #include"iostream"
  3. #include <string.h>
  4. using namespace std;
  5. int count; //统计全排列的个数
  6. void swap(char *str, int a, int b)
  7. {
  8. char temp;
  9. temp = str[a];
  10. str[a] = str[b];
  11. str[b] = temp;
  12. }
  13. int is_swap(char *str, int begin, int k)
  14. {
  15. int i, flag;
  16. for (i = begin, flag = 1; i < k; i ++) {
  17. if (str[i] == str[k]) {
  18. flag = 0;
  19. break;
  20. }
  21. }
  22. return flag;
  23. }
  24. void permutation_process(char *str, int begin, int end) {
  25. int k;
  26. if (begin == end - 1) {
  27. cout<<"第"<<count++<<"个全排列"<<str<<endl;
  28. }else {
  29. <span style="white-space:pre"> </span> for (k = begin; k < end; k ++) {
  30. <span style="white-space:pre"> </span> if(is_swap(str, begin, k) ){
  31. <span style="white-space:pre"> </span>swap(str, k, begin);
  32. <span style="white-space:pre"> </span>permutation_process(str, begin + 1, end);
  33. <span style="white-space:pre"> </span>swap(str, k, begin);
  34. }
  35. }
  36. <span style="white-space:pre"> </span> }
  37. }
  38. int _tmain(int argc, _TCHAR* argv[])
  39. {
  40. int i, len;
  41. cin>>str;
  42. count = 1;
  43. len = strlen(str);
  44. permutation_process(str, 0, len);
  45. return 0;
  46. }

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,下面看代码分析
  1. //全排列非递归
  2. #include"stdafx.h"
  3. #include<iostream>
  4. #include<algorithm>
  5. #include<cstring>
  6. using namespace std;
  7. #include<assert.h>
  8. //反转区间
  9. void Reverse(char* pBegin , char* pEnd)
  10. {
  11. while(pBegin < pEnd)
  12. swap(*pBegin++ , *pEnd--);
  13. }
  14. //下一个排列
  15. bool Next_permutation(char a[])
  16. {
  17. assert(a);
  18. char *p , *q , *pFind;
  19. char *pEnd = a + strlen(a) - 1;
  20. if(a == pEnd)
  21. return false;
  22. p = pEnd;
  23. while(p != a)
  24. {
  25. q = p;
  26. p--;
  27. if(*p < *q) //找降序的相邻2数,前一个数即替换数
  28. {
  29. //从后向前找比替换点大的第一个数
  30. pFind = pEnd;
  31. while(*pFind < *p)
  32. --pFind;
  33. swap(*p , *pFind);
  34. //替换点后的数全部反转
  35. Reverse(q , pEnd);
  36. return true;
  37. }
  38. }
  39. Reverse(a , pEnd); //如果没有下一个排列,全部反转后返回false
  40. return false;
  41. }
  42. int cmp(const void *a,const void *b)
  43. {
  44. return int(*(char *)a - *(char *)b);
  45. }
  46. int main(void)
  47. {
  48. char str[] = "bac";
  49. int num = 1;
  50. qsort(str , strlen(str),sizeof(char),cmp);
  51. do
  52. {
  53. printf("第%d个排列\t%s\n",num++,str);
  54. }while(Next_permutation(str));
  55. return 0;
  56. }


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小桥流水78/article/detail/825990
推荐阅读
相关标签
  

闽ICP备14008679号