N个字符全排列的递归实现

来源:互联网 发布:网络红歌2016流行dj 编辑:程序博客网 时间:2024/06/05 09:36

递归算法的一个重要思想就是把规模大的问题向递归结束的条件靠拢。但是递归函数的原型和非终止条件下的递归怎么调用,是比较难的。

个人觉得,可以从递归执行的次数或结果的个数的计算来帮助怎么写递归的设计。如f(n)=f(n-1)+f(n-2)这种方式的递归,每个求f(n)就只要一个f(n-1),一个f(n-2).

而汉诺塔问题(http://blog.csdn.net/lin200753/article/details/27579481):f(n)=2*f(n-1)+1,则在处理第n次时,就要两次用于f(n-1)。

而全排列的递归也是类似的,f(n)=n*f(n-1),从而在第n次处理就要用于n次f(n-1),从而在非递归情况下就要用到一个长度可变的循环。循环的长度就是n。

(1)如果长度为1的字符串,则直接输出,它的全排序只有一个。

(2)len=2:如src[]=ab,则以a为排头,后面为b,则长度往(1)靠拢,输出src,

以b为排头,相当于将src[0]与src[1]交换得到src[]=ba,后面长度往(1)靠拢,输出src.

(3)len=3,如src[]=abc,则以a为排头,后面长度为2,往(2)靠拢,可以得到abc acb

以b为排头,相当于将src[0]与src[1]交换得到src[]=bac,后面长度为2,往(2)靠拢,可以得到bac bca

以c为排头,相当于将src[0]与src[2]交换得到src[]=cba,后面长度为2,往(2)靠拢,可以得到cba cab

len=4,,,,等情况类似,在以某个字符为排头结束后,要把src恢复到最原始的src,即要再进行一次交换。

这里先不考虑src中出现重复字符的情况。

从而若src的长度为n,则要进行src[0]与src[0],src[0]与src[1],......src[0]与src[n-1]进行n次交换。第一次交换相当于没有交换,只是以最开始的字符为排头,然后把长度减一进行递归处理。

#include "stdio.h"#include"string.h"#define DEBUG 1/*采用do..while(0)的方式*/#define SWAP(x,y) do{\int z=(x);\(x)=(y);\(y)=z;\}while(0)


/***全排列,有bug,不能有重复的字符。**/static int gen_allrang3(char *src,int s,int e,char *dest){int i;static count=0;if(s==e){strcat(dest,src);strcat(dest,",");printf("in gen3第%d个为:%s\n",++count,src);}else{  for(i=s;i<=e;i++){SWAP(src[s],src[i]);gen_allrang3(src,s+1,e,dest);SWAP(src[s],src[i]);  } } return count;}/*对gen_allrang3进行封装*/int gen_allrang_API(char *src,char *dest){int len;int count;if(NULL==src||NULL==dest)return -1;len=strlen(src);count=gen_allrang3(src,0,len-1,dest);dest[strlen(dest)-1]='\0';return count;}int gen_allrang4(char *src,int s,int e){int i;static count=0;if(s==e){printf("in gen4第%d个为:%s\n",++count,src);}else{  for(i=s;i<=e;i++){SWAP(src[s],src[i]);gen_allrang4(src,s+1,e);SWAP(src[s],src[i]);  }}return count;}void main(){    char src[]="abc";    char dest[1000];    int sum;    *dest='\0';       //sum=gen_allrang3(src,0,strlen(src)-1,dest);    sum=gen_allrang_API(src,dest);    printf("count=%d,dest=%s\n",sum,dest);}

——————————————————————————————————————————————————————

而去除重复字符,之前有一种错误的想法:就是在src[s] 与src[i]交换的时候,若src[i]==src[s]就不交换,从而也不进入递归,即这个排头已经出现过了。之所以是错的,在于我们都是把src[s]与后面的交换,从而如src[]=abb,而src[0]!=src[1]  src[0]!=src[2];

正确的方式是:当src[s],要与src[i],交换时,对s->i之间的字符进行查重,即在s->i之间(没有包括i),若已经出现了src[i],则不要交换。

/*若没有与src[e]重复的字符,则返回1,否则返回0*/int no_rep_to_e(char *src,int s,int e){int i;for(i=s;i<e;i++)if(src[i]!=src[e])continue;elsereturn 0;return 1;}int gen_allrang5(char *src,int s,int e){int i;static count=0;if(s==e){printf("in gen5第%d个为:%s\n",++count,src);}else{  for(i=s;i<=e;i++){  if(no_rep_to_e(src,s,i))  {SWAP(src[s],src[i]);gen_allrang5(src,s+1,e);SWAP(src[s],src[i]);  }  }}return count;}void main(){    char src[]="abb";    char dest[1000];    int sum;    *dest='\0';       //sum=gen_allrang3(src,0,strlen(src)-1,dest);   // sum=gen_allrang_API(src,dest);   gen_allrang5(src,0,strlen(src)-1);   }

in gen5第1个为:abb
in gen5第2个为:bab
in gen5第3个为:bba
             Press any key to continue

若为1234,则相当于A44,=4*3*2*1=24种,这种递归不是字典序的。

in gen5第1个为:1234in gen5第2个为:1243in gen5第3个为:1324in gen5第4个为:1342in gen5第5个为:1432in gen5第6个为:1423in gen5第7个为:2134in gen5第8个为:2143in gen5第9个为:2314in gen5第10个为:2341in gen5第11个为:2431in gen5第12个为:2413in gen5第13个为:3214in gen5第14个为:3241in gen5第15个为:3124in gen5第16个为:3142in gen5第17个为:3412in gen5第18个为:3421in gen5第19个为:4231in gen5第20个为:4213in gen5第21个为:4321in gen5第22个为:4312in gen5第23个为:4132in gen5第24个为:4123             Press any key to continue



0 0
原创粉丝点击