您的位置:首页 > 其它

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

2016-08-06 20:04 253 查看

问题:

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

1.全排列的递归实现

思路

首先,我们固定第一个字符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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: