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

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

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

而汉诺塔问题(http://blog.youkuaiyun.com/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;
		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



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值