递归详解(一)全排列问题 数据结构-算法解析-浙大-PAT

递归——全排列问题(Full Permutation)

​ 废话少说,首先看定义:

全排列(Full Permutation),一般把1~n这n个整数按某个顺序摆放的结果称为这n个整数的一个排列 ,而全排列指这n个整数能形成的所有排列。例如对1、2、3这三个整数来说, (1, 2, 3)、(1, 3, 2)、(2, 1, 3)、(2, 3, 1)、(3, 1, 2)、(3, 2, 1)就是1~3的全排列

​ 现在需要实现按字典序从小到大的顺序输出 1~n 的全排列,其中 (a1, a2, …, an) 的字典序小于 (b1, b2, …, bn) 是指存在一个 i ,使得 a1=b1, a2=b2, …, a(i-1) = b(i-1)、ai<bi 成立。因此上面n=3的例子就是按字典序从小到大的顺序给出的。

​ 按字典序排列用大白话的意思就是:任取排列结果中的两个元素作比较,例如(2, 1, 3)和(2, 3, 1),两个元素的第一位相等都是2(比较下一位),第二位 1 < 3,所以按从小到大的顺序排列时, (2, 1, 3)一定排在(2, 3, 1)前面。

​ 用递归的角度去考虑,如果把问题描述成

”输出1~n这n个整数的全排列“

​ 那么它就可以被分为若干个子问题:

”输出以1开头的全排列“、”输出以2开头的全排列“ … ”输出以n开头的全排列“。

在实现代码前,我们需梳理一下重要逻辑

  1. 我们需要一个int型数组 P, 来存放当前的排列;
  2. 排列中的元素不能重复,例如1~3的全排列,是不能出现(1,2,2)这样的元素的,每个数字能且仅能出现一次。于是我们可以设定一个bool型的数组 Table[x],数组的下标代表当前的数字,默认值为false,当整数x已经在当前排列中使用过的时候,我们就将 Table[x] 的值设置为true。
  3. 当我们按顺序往P的第1位到第n位中填入数字的时候,不妨假设当前已经填好了P[1] ~ P[index-1],正准备填P[index]。显然我们需要for循环遍历1~n,来确定当前的数字x是否在已经枚举的数字中出现过(即Table[x] == false)。若出现过,我们就跳过这个数字去看下一个数字Table[x+1],若没有出现过,就把他填入P[index],同时将Table[x]的值置为true,然后进入递归去处理第index+1位;当排列的全部递归完成时,再将Table[x]的所有值还原为false,以便下一次for循环的判断。
  4. 在递归进行的最后的时候,我们需要规定一个跳出的边界的条件。当P的index到达 n+1 时,说明P的第1~n位都已经填好了,此时便可跳出递归。

如果你已经理解了上述的逻辑推理,可以试着自己先用代码实现一下,如果还未理解,一定要多看几遍,自己思考之后再往下看。

注释很重要,请务必认真阅读。

#include <cstdio>
const int maxn = 11;
int n;				//计算1~n的全排列 
int P[maxn];		//P为当前排列 
bool table[maxn] = {false};	//table记录整数x是否已经在P中 

void generateP(int index) {
	//首先判断是否已经到达了递归边界
	if(index == n + 1) {	//递归边界,已经处理完成排列的1~n位 
		for(int i = 1; i <= n; i++) {
			printf("%d", P[i]);		//输出当前排列 
		}
		printf("\n");
		return;
	} 
	//如若还没有到达边界,开始遍历table数组 
	for(int x = 1; x <= n; x++) {
		if(table[x] == false) {	//判断当前数字x是否已被使用,若没有 
			P[index] = x;		//令P的第index位为x,即=把x加入当前排列
			table[x] = true;	//标记数字x已被使用
			generateP(index + 1);	//递归调用,处理排列的第index+1号位
			table[x] = false;		//全部梳理完后,递归依次弹出,还原x的状态 
		} 
	} 
} 

int main() {
	n = 3;			//欲输出1~3的全排列
	generateP(1);	//从P[1]开始填(递归调用)
	return 0; 
}

如果你有认真阅读上述的逻辑推理,相信你一定写的出和我类似的代码。

每日进步一点点,

我们下篇再见,

共勉。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值