字符串的排列,递归输出

来源:互联网 发布:足球战术软件 编辑:程序博客网 时间:2024/06/11 04:23

(先稳定一下军心,下面所有代码都测试过,可以直接用,所以就没有截图,想看效果的可以自己运行一下)

1.全排列

全排列:字符以任意顺序排列,比如123的全排列是123,132,213,231,312,321。长度为n的字符串的全排列个数为n!个,下面是代码:

/*思路:s全排列是第一位与s[1~n-1]的全排列,所以每次交换第一位与** s中的某一位,然后递归求出s[1~n-1]的全排列。*/#include<iostream>#include <string>using namespace std;static int count=0;void Permutation(string s,int beg,int sLen){if(0==sLen)  //这轮全排列结束{++count;cout<<s<<endl;return;}for (int i=0;i<sLen;++i){swap(s[beg],s[beg+i]);      //将s中的每一位与第一位交换Permutation(s,beg+1,sLen-1); //排列s[1~n-1]swap(s[beg+i],s[beg]);     //每一轮完后将交换后的字符还原}}int main(){string s;cout<<"请输入字符串:";cin>>s;int sLen=s.size();Permutation(s,0,sLen);cout<<endl<<"共"<<count<<"个"<<endl<<endl;return 0;}


代码中的Permutation换成下面的代码,效果也一样:

void Permutation(string s,int beg,int end){if(beg==end-1)  //这轮全排列结束{++count;cout<<s<<endl;return;}for (int i=beg;i<end;++i){swap(s[beg],s[i]);Permutation(s,beg+1,end);swap(s[i],s[beg]);}}


在上面的代码中,如果字符串中有重复的字符,就不能避免重复输出了。例如对于1213,n!=4!=24,如果考虑到避免重复输出,应该有4!/2!=12个。下面的代码是避免全排列中的重复输出:

/*思路:1.轮流选取s中的每位作为第一位。如果第i位字符在[0,i-1]这个区间内出现过,**就不再对其进行排列,因为把它放在第一位的所有排列在前面排列过了(就是代码中j作为循环变量的作用)。**2.把当前选取的这一位放入目标数组中。**3.用一个临时数组保存剩余的字符,并把其作为源。同时目标数组的地址下移一位,因为当前位已经排列好。*/#include<iostream>#include <string>using namespace std;static int count=0;  //记录全排列个数char  *result=new char[16]();     //存放某个排列void Permutation(string strSource,char *strResult,int sLen){if(sLen==1)  //这轮全排列结束{strResult[0]=strSource[0];   //只有一个字符++count; //strResult在递归的过程中是一次向后移一位的,所以字符串的首地址是resultcout<<result<<endl;    return;}for (int i=0;i<sLen;++i){int j;for ( j=0;j<i&&strSource[j]!=strSource[i];j++);    //判断当前字符是否在之前已经出现过if (j==i)      //没有出现过{strResult[0]=strSource[i];    //把当前字符放在第一位//将除了当前位剩下的字符存入nextRecursion中,进入下一次递归string nextRecursion=strSource.substr(0,i)+strSource.substr(i+1,sLen-i-1); Permutation(nextRecursion,strResult+1,sLen-1); //strResult向后移一位,构造下个字符}}}int main(){string s;cout<<"请输入字符串:";cin>>s;int sLen=s.size();Permutation(s,result,sLen);cout<<endl<<"共"<<count<<"个"<<endl<<endl;return 0;}


2.排列

在第一节中讨论了字符串全排列的问题,在这一节讨论排列的问题。

排列:从长度为n的字符串中任意挑选m个元素以任意次序排列。全排列是n=m的特殊情况。

编程思路和上面的代码很像。最大的不同之处就是把结束时的情况放到了for循环里面,并且把return去掉了,这是为了保证所有的情况都能遍历到。还增加了一个参数k来记录已经取得的字符个数。

#include<iostream>#include <string>using namespace std;static int count=0;  //记录全排列个数char  *result=new char[16]();     //存放某个排列//sLen是源字符串的长度,m是要取的字符个数,所以排列个数是A(sLen,m)//k为已经取得的字符个数void Permutation(string strSource,char *strResult,int sLen,int m,int k){for (int i=0;i<sLen;++i){int j;for ( j=0;j<k&&result[j]!=strSource[i];j++);    //判断当前字符是否在之前已经出现过if (j==k)      //没有出现过{if(k==m-1) //这轮全排列结束{strResult[0]=strSource[i];   ++count;//strResult在递归的过程中是一次向后移一位的,所以字符串的首地址是resultcout<<result<<endl;    //return;}else{strResult[0]=strSource[i];    //把当前字符放在第一位//将除了当前位剩下的字符存入nextRecursion中,进入下一次递归string nextRecursion=strSource.substr(0,i)+strSource.substr(i+1,sLen-i-1); Permutation(nextRecursion,strResult+1,sLen-1,m,k+1);  //strResult向后移一位}}}}int main(){string s;int m;cout<<"请输入字符串:";cin>>s;cout<<"请输入m:";cin>>m;int sLen=s.size();if(m<=sLen)Permutation(s,result,sLen,m,0);cout<<endl<<"共"<<count<<"个"<<endl<<endl;return 0;}

ps:为了和之前的代码保持连贯性,并且突出修改的重点(1.终止条件放到for循环中;2.去掉return),代码中的一些优化我都没有改。比如说,可以把if和esle中的strResult[0]=strSource[i]; 提出来;28行和29行的截取字符串是多余的,因为13行的代码已经判断重复问题了,所以28行和29行直接可以换成Permutation(strSource,strResult+1,sLen,m,k+1);

这个代码和上面的代码很像,这里可以好好体会一下这两个代码的不同之处。为了和数学中的排列定义A(n,m)相符,我把代码中的n换成了m,sLen相当于这里的n 。还需要说明的一点就是这段代码只能运用在无重复字符的字符串中,若有重复字符,会有重复输出。




0 0
原创粉丝点击