【算法笔记】递归之全排列|学习感受|总结

1.全排列:本文以(1,2,3)为例,共有六种情况:123,132,213,231,312,321

2.递归思路:

此问题可以拆分为求以1开头的全排,以2开头的全排,以3开头的全排。

假设在当前的排列P(1,2,x)中,前两个数字已经排好了,所以只需要在第三个位置插入一个数字x,易知此处应对1~3这三个数字进行枚举,因为前两个数字已经使用过了,所以这里只能使用3.(具体的实现细节后面介绍).用P[index]表示当前要填入位置的数字,index用来表示位置。

再确定一个散列表“hashtable[]=1”用来标注刚刚填入的数字已经被使用过了,比如hashtable[1]=1,表示1这个数字已经被使用过,后面的就不能再用了,借助一个if判断即可实现。若这个数字没被使用,当P[index]被填入数字后,需要递归调用generateP函数,继续进行下一位,当到达递归边界:即index=n+1时,说明三个位置已经全部填完了,这时候输出这个排列即可。

递归条件:generateP(index+1)

递归边界 : index+1 == n

笔者在初学时,由于未完全理解递归的调用过程,故无法理解代码。该递归的具体过程为:

主函数运行generateP(1),调用函数,未到递归边界,进入for循环。

枚举1,因为1这个数字还未使用(hashtable[x]=false),把x=1填入当前位置,标记1已使用,递归调用generateP(2),处理第二个位置,未到递归边界,再进入for循环,枚举1,由于hashtable[1]=true,说明1已被使用,if判断条件不成立,i自增,枚举2,2未被使用,继续下面的语句。

程序不断递归,直到在generateP(3)中运行到generateP(4),到达递归边界,把当前排列123输出。输出后返回,回到调用generateP(4)的位置即generateP(3),继续下面的语句,即hashtable[3]=false.一旦 for 循环结束generateP(3) 的执行也就结束了,程序会返回到上一级调用,即 generateP(2)generateP(2)中,继续运行下一句hashtable,然后注意,此时x=2,还可以枚举x=3,不理解这个过程的可以想一下:第二个位置一共有2种可能,所以回到generateP(2),会进行x=3的循环,由于hashtable[2]已经被恢复,所以这里可填入x=3,继续递归generateP(3),进行第三个位置的枚举。此时1,3这两个数字都已经被使用,所以只能填入2.

至此,132已被填入,以1开头的排列已输出完毕,for循环中x自增到2,继续以2开头的处理。

PS:读者在阅读本文时,可对照下方代码以及注释逐步理解。笔者水平有限,难免存在错误,欢迎指正。

#include<cstdio>
void generateP(int index);
const int maxn = 11;
int n, P[maxn], hashTable[maxn] = {false};
int main() {
	n = 3;//需要进行排列的数字个数
	generateP(1);//从第一个位置开始处理
	return 0;
}
void generateP(int index)
{
	int x;
	if (index == n+1)//递归边界
	{
	for (int i = 1; i <= n; i++)
		printf("%d ",P[i]);
		printf("\n");
		return;
	}//输出当前排列

		for (x = 1; x <= n; x++)
		{//x是要填入的数字,用循环进行枚举,选择要填入的数字
			if (hashTable[x] == false)//如果x还没有被使用
			{
				P[index] = x;//把x填入当前位置
				hashTable[x] = true;//x填入后把它标记为ture
				generateP(index + 1);//继续处理下一个位置 递归调用
				hashTable[x] = false;//当前位置处理完毕 把标记还原
			}
		}
	
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值