全排列 递归+回溯

对于全排列问题,我们举下面一个例子来分析——

一个char型数组比如有5位,ABCDE,生成其全排列的所有情况,如果用递归的话思路就会非常简单。

递归思想说白了就两步:

1.将第一个字符与后面的字符依次交换顺序;

2.将皮球踢给后来人,即将去掉第一个字符的剩余字符数组看成一个新的数组,并对其进行和之前相同的操作。

贴出代码来瞅瞅:

#include<stdio.h>
#include<string.h>

void f(char *a, int k)
{
	int i;
	char tmp;
	if(k ==  strlen(a))
	{
		puts(a);
	}

	for(i = k; i < strlen(a); i++)                            //k是被交换的位置
	{
		tmp = a[k]; a[k] = a[i]; a[i] = tmp;		//将第k位与后面的依次交换(比如将首字母和第3个交换)
		f(a, k+1);                                  //然后将皮球踢给去掉第k位的剩余部分(去掉首字母的部分)
		tmp = a[k]; a[k] = a[i]; a[i] = tmp;        //回溯,和刚刚交换的那位换回来(比如和第3位换回来),然后在下一轮循环中才能和后面的(第4位)进行交换
	}
}

int main()
{
	char a[] = {'A', 'B', 'C', '\0'};
	f(a, 0);
	return 0;
}


另一种全排列和这个几乎一样,比如“输入一个数字(1-10中的某一个),输出1到这个数字的全排列”,代码如下:

#include<stdio.h>
#define MAX 10				
char stack[MAX+1]={0};						//以字符串的形式存放生成的全排列数
int num[MAX]={0};
int N;
void allrank(int m,int n);					//全排列数生成函数

int main()
{
	int i=0;
	printf("Input a number N(1<=N<=10):");
	scanf("%d",&N);
	allrank(0,N);
	return 0;
}

void allrank(int m,int n)
{
	int i=0;
	if(n==0)					//n是剩余的没被标记(使用)的数,如果n=0,意味着数字被用完了
	{
		stack[N+1]='\0';
		puts(stack);
		return;
	}
	for(i=1;i<=N;i++)
	{
		if(num[i]==0)
		{
			num[i]=1;				//数字i被标记为已使用
			stack[m]='0'+i;
			allrank(m+1,n-1);		//进入了另一个函数
			num[i]=0;				//数字i被释放  【回溯】
		}
	}
}



我们来分析一下上面的代码,主要分析allrank里面的部分,举个栗子来分析吧:

比如N=4,则刚开始i=1,标记为用了,进入内一层函数;
再i=1,发现已经被用了,i++,变为2,把2标记为用了,然后再进入内一层;
同理,i=1,发现被用了,i++,还被用了,i++,变为3,把3标记为用了,进入内一层;
i=1,用了,++,++,++,i=4,发现还没被用,然后把4标记为用了,进入内一层;
此时n=0了,生成一个全排列数1234,return回上一层函数(这个函数里的i的生存期结束,此时上一层函数的i为4);
IMPORTANT----------然后运行第33行代码num[i]=0,则4被释放,i++,则i变为5,跳出循环;
IMPORTANT----------接着函数运行结束(这个函数是void类型,不需要return返回),直接回到了再上一层;
也就是回到了上一层函数的fullrank(m+1,n-1)这一行,此时i=3,接着运行num[i]=0,释放3,i++,变为4,继续进入for循环,if(num[4]==0)为真;
也就是说4没有被使用,把4标记,(此时四位数的前三位确定,为124_),进入内层,i从1循环,发现3没有被用,把3用了,此时变为1243,输出;
之后就不说了。

递归+回溯,其实全排列就是这样。

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值