循环和递归解决全排列问题

首先先帮大家了解一下数字的全排列,数字的全排列在我看来就是把给出的若干数字按照不同的顺序就行排序,比如说:{1,2,3}这三个数字就行全排列我们可以排出{1,2,3},{1,3,2},{2,1,3},{2,3,1},{3,2,1},{3,1,2}

       方法一:因为给的数值比较小所以我们可以用一个笨办法(同时也是一个容易理解的方法)解决。也就是枚举法。

int main()
{
	for(int a=1;a<4;a++)
	{
		for(int b=1;b<4;b++)
		{
			for(int c=1;c<4;c++)
			{
				if(a!=b&&b!=c&&a!=c)//排列特征就是互相不相等
				{
					printf("%d %d %d\n",a,b,c);
				}
			}
		}
	}
}

       大家可以看出我们是用多重嵌套的方法列出所有可能,然后筛选出我们要的那部分,可以说枚举是大家都能理解的,但是时间复杂度也高,同时实用性差一点,因为这个题我们只给了3个数我们写了三个循环,如果给10个数我们就要写10个循环,时间复杂度也太高了。虽说枚举可以解决但这道题不提倡使用。

      那么问题来了,如何输入一个数实现1~n的全排列呢?

      我们可以这样想,比如说说我们现在有三个盒子编号分别是1,2,3。然后我们手里有三张扑克牌分别是1,2,3。现在我们要把扑克牌放入箱子里面并且每一个盒子只能放一张扑克牌,那么有多少种方法呢?

     首先我们来到1号盒子面前,这时候我们心想该放哪张扑克牌呢?不如我们就规定一个顺序:无论走到哪个箱子我们都按照1,2,3的顺序放扑克牌。也就是先放1号扑克牌,再放2号扑克牌,再放3号扑克牌。

首先我们来到了1号箱子,按照我们的规定先把1号扑克牌放入1号箱子

然后我们来到了2号箱子,按照规定我们应该放1号扑克牌,但是手里现在只有2号和3号扑克牌,所有我们往2号箱子放2号扑克牌。 接着来到3号箱子,按照规定应该先放1号扑克牌再放2号扑克牌,但现在只有3号扑克牌所有我们把3号扑克牌放入3号箱子。

     现在我们扑克牌放完了,这时箱子里的数字就是我们排列出来的序列。也就是{1,2,3}但是现在还没有结束,当我们产生了一种序列后我们需要返回。

     首先我们返回3号箱子,取出3号扑克牌后看看能不能放入其它扑克牌从而产生新的序列,当取出3号扑克牌后我们手里只有一张3号扑克牌所以没有别的选择,所以再次返回。

     返回到2号箱子,当取出2号扑克牌后手里就有两张牌了,这时候按照之前约定的顺序往2号箱子放入3号扑克牌(因为2号扑克牌之前已经放过了所以这次放3号扑克牌),放好以后向后走一步把仅剩的一张2号牌放入3号箱子中。这时候我们就产生了一种新的序列也就是{1,3,2}。

     接下来我们只需要按照上述方法模拟出{2,1,3},{2,3,1},{3,2,1},{3,1,2}就好了,现在我们基本的逻辑也就理解了,开始解决代码问题。

#include<iostream>
using namesqace std;
int book[10], a[10],n;//全局变量定义数组默认为0;所以不用再赋初始值。数组a代表盒子,数组book用来标记扑克牌有没有用过,因为默认为0,所以book[i]==0说明没用过,book[i]==1说明使用过
void test(int step)//step表示现在正处在第几个盒子
{
	if(step==n+1)//一共有n个盒子,如果现在处于n+1个盒子说明前n个盒子已经都放入了卡片,所以我们执行打印
	{
		for(int i=1;i<=n;i++)
		{
			printf("%d ",a[i]);
		}
		printf("\n");
		return;//得到一个序列以后返回上一步
	}
		for(int i=1;i<=n;i++)//i代表我们扑克牌的编号
		{
			//首先判断扑克牌i号扑克牌在不在手上
			if(book[i]==0)//book[1]==0说明在手上,说明有说到过
			{
				a[step]=i;//因为现在处理的是第step盒子,所以把手上的这张放入该盒子中
				book[i]=1;//因为i代表扑克牌编号,并且现在i号扑克牌放入了盒子中,所以i号扑克牌后面不能再用了,所以把book[i]=1;
				test(step+1);//按照逻辑盒子放入扑克牌后我们处理下一个盒子。递归调用
				book[i]=0;//放完以后要收回扑克牌从而进行下一次排列
			}
		}
	return;
}
int main()
{
	scanf("%d",&n);//输入1~9之间的数字
	test(1);
}

 我们上述代码通过递归调用实现了9以内的数字全排列,但有同学想问了,如果要求实现任意数字的全排列怎么办?

 要对数组中的数字进行全排列有这样一个经典的例题:

设R={r1,r2,...,rn}(r后面是下标)是要进行排列的n个元素,Ri=R-{ri}.集合X中元素的全排列记为perm(X).(ri)perm(X)表示在全排列perm(x)的每一个排列前加上前缀ri得到的排列.R的全排列可归纳定义如下:
当n=1时,perm(R)=(r),其中r是集合中唯一的元素;
当n>1时,perm(R)由(r1)perm(R1),(r2)perm(R2),...(rn)perm(Rn)构成.

要解决这个题首先大家明确一下Ri=R-{ri}这个意思。这代表在集合中除去i元素,比如,R={ri,r2,r3},此时R1=R-{r1}={r2,r3}。

下面给大家看看我自己的思路理解,写的不好比较丑陋大家多多包涵

下面上代码

#include<iostream>
using namespace std;
void pai(int arr[],int k,int m)
{
	int j;
	if(k==m)//当l==m证明规模为0执行打印
	{
		for(j=0;j<=m;j++)
		{
			printf("%d ",arr[j]);
		}
		printf("\n");
	}
	else
	{
	for(j=k;j<=m;j++)
    {
		swap(arr[j],arr[k]);//交换下标j和k对应的值
		pai(arr,k+1,m);//规模减小,再次处理小规模
		swap(arr[j],arr[k]);//再次交换回来
	}
	}

}
int main()
{
	int arr[3]={1,2,3};
	pai(arr,0,2);
}

    如果大家看不懂代码请认真的看一下思路图,这就是一个交换和回退的过程。希望大家通过这个题理解递归的使用

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值