【算法】递归

本文为算法的学习笔记,讲解递归。欢迎交流。

分治

分治法将原问题划分为子若干规模较小但与原问题同构的子问题,分别解决后再合并解。子问题应该相互独立、没有交叉。当子问题个数为 1 时称为减治,如求 n 的阶乘;子问题个数大于 1 称为分治,如斐波那契数列。

分治法是一种算法思想,可以用非递归或递归方法解决,但因为递归实现较为容易。

递归

递归在于反复调用函数,把问题范围缩小,直到可以直接得到边界数据的结果,然后在返回的路上求出对应的解。

递归中两个重要的概念:

  • 递归式:将问题分解为子问题,减少数据规模并向下一层递归
  • 递归边界:是分解的尽头,用来返回最简单底层的结果

全排列

我们要以字典序从小到大的顺序输出 n 个整数的全排列。

思路

设数组 P 存放当前排列,设散列数组 hashTable 用来记录数字是否已经放入过 P

代码

#include 
using namespace std;

const int maxn = 11;
// P为当前排列,hashTable记录整数i是否已经在P中
int n, P[maxn], hashTable[maxn] = { false };
// 当前处理排列的第index号位
void generateP(int index) {
	if (index == n + 1) { // 递归边界
		for (int i = 1; i <= n; ++i) // 输出当前排列
			printf("%d", P[i]);
		printf("\n");
		return;
	}
	for (int i = 1; i <= n; ++i) // 枚举1-n,填入P[index]
		if (hashTable[i] == false) { // i可被填入
			P[index] = i; // 填入i
			hashTable[i] = true;
			generateP(index + 1); // 处理排列的第index+1位
			hashTable[i] = false; // 还原状态
		}
}

int main() {
	// 注意!!这里不要重定义n,否则全局的n会被初始化为0
	n = 3; // 生成1-3全排列
	generateP(1); // 从P[1]开始填
	return 0;
}

n 皇后问题

n 皇后问题指,在一个 n*n 的国际象棋棋盘上放置 n 个皇后,使得这 n 个皇后两两不在同一行、同一列、同一对角线,求合法的方案数。下图是 n=5 时的情况,左图合法,右图不合法:

【算法】递归_第1张图片

思路

因为每行每列只能放一个皇后,我们把 n 列皇后所在的行号依次写出,是一个 1-n 的全排列,我们只需要查看每个排列是否合法即可。

代码

const int maxn = 11;
// P为当前排列,hashTable记录整数i是否已经在P中
int n;
int ans = 0;
int P[maxn], hashTable[maxn];
// 当前处理排列的第index号位
void generateP(int index) {
	if (index == n + 1) { // 递归边界
		bool flag = true; // flag为true表示合法方案
		for (int i = 1; i <= n; ++i) // 遍历任意两个皇后
			for (int j = i + 1; j <= n; ++j)
				if (abs(i - j) == abs(P[i] - P[j])) // 行坐标之差与纵坐标之差相等,在一条对角线上。
					flag = false; // 不合法
		if (flag) ++ans; // 当前方案合法则+1
		return;
	}
	for (int i = 1; i <= n; ++i) // 生成全排列
		if (hashTable[i] == false) {
			P[index] = i; // 填入i
			hashTable[i] = true;
			generateP(index + 1);
			hashTable[i] = false;
		}
}

上面这种枚举所有情况然后判断每种情况合法性的方法称为暴力法。如果当前位置的皇后与前面的已经冲突了,此时无需继续递归。这种方法称为回溯法

const int maxn = 11;
// P为当前排列,hashTable记录整数i是否已经在P中
int n;
int ans = 0;
int P[maxn], hashTable[maxn];
// 当前处理排列的第index号位
void generateP(int index) {
	if (index == n + 1) { // 递归边界
		++ans; // 能到达这里一定是合法的。
		return;
	}
	for (int i = 1; i <= n; ++i) // 第i行
		if (!hashTable[i]) { // 第i行还没有皇后
			bool flag = true; // flag为true表示当前皇后与之前的无冲突
			for (int pre = 1; pre < index; ++pre) // 遍历之前的皇后
				// 第index列皇后的行号为i,第pre列皇后的行号为P[pre]
				if (abs(index - pre) == abs(i - P[pre])) {
					flag = false; // 与之前皇后有对角线冲突
					break;
				}
			if (flag) { // 无冲突,把皇后放到第i行
				P[index] = i; // 令第index列皇后的行号为i
				hashTable[i] = true; // 标记第i行已被占用
				generateP(index + 1); // 递归
				hashTable[i] = false; // 还原
			}
		}
}

你可能感兴趣的:(数据结构与算法,算法,数据结构,递归法,分治算法)