N个字符全排列的递归实现
2014-05-30 20:47
190 查看
递归算法的一个重要思想就是把规模大的问题向递归结束的条件靠拢。但是递归函数的原型和非终止条件下的递归怎么调用,是比较难的。
个人觉得,可以从递归执行的次数或结果的个数的计算来帮助怎么写递归的设计。如f(n)=f(n-1)+f(n-2)这种方式的递归,每个求f(n)就只要一个f(n-1),一个f(n-2).
而汉诺塔问题(/article/11435162.html):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次交换。第一次交换相当于没有交换,只是以最开始的字符为排头,然后把长度减一进行递归处理。
——————————————————————————————————————————————————————
而去除重复字符,之前有一种错误的想法:就是在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],则不要交换。
in gen5第1个为:abb
in gen5第2个为:bab
in gen5第3个为:bba
Press any key to continue
若为1234,则相当于A44,=4*3*2*1=24种,这种递归不是字典序的。
个人觉得,可以从递归执行的次数或结果的个数的计算来帮助怎么写递归的设计。如f(n)=f(n-1)+f(n-2)这种方式的递归,每个求f(n)就只要一个f(n-1),一个f(n-2).
而汉诺塔问题(/article/11435162.html):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; else return 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个为:1234 in gen5第2个为:1243 in gen5第3个为:1324 in gen5第4个为:1342 in gen5第5个为:1432 in gen5第6个为:1423 in gen5第7个为:2134 in gen5第8个为:2143 in gen5第9个为:2314 in gen5第10个为:2341 in gen5第11个为:2431 in gen5第12个为:2413 in gen5第13个为:3214 in gen5第14个为:3241 in gen5第15个为:3124 in gen5第16个为:3142 in gen5第17个为:3412 in gen5第18个为:3421 in gen5第19个为:4231 in gen5第20个为:4213 in gen5第21个为:4321 in gen5第22个为:4312 in gen5第23个为:4132 in gen5第24个为:4123 Press any key to continue
相关文章推荐
- N个字符全排列的非递归实现
- 递归实现的字符全排列
- 8.n个字符的全排列(递归实现)
- 递归实现n个不同字符的所有全排列
- 算法——全排列递归实现
- 递归实现全排列
- C++全排列递归实现
- 非递归实现不重复序列的全排列(二)
- 全排列的递归实现
- 也论全排列的递归实现
- 简单全排列C递归实现 没考虑重复情况
- 简单全排列C递归实现 没考虑重复情况
- 数字全排列的递归和非递归实现(C/C++)
- 将十进制数转换为指定进制的数 不使用字符操作和递归实现
- 全排列各种实现(非递归、递归)
- 递归实现全排列
- C语言实现全排列(部分算法参考网友,可实现重复字符的组合)
- 全排列递归实现的讨论
- 非递归实现不重复序列的全排列(二)
- 用递归实现全排列