《算法笔记》第4章 入门篇(2)—算法初步
4.3递归
4.3.1分治
无(O_O)
4.3.2递归
1.使用递归求解n的阶乘
代码如下:
#include<cstdio>
using namespace std;
//n!的计算式:1*2*3*...*(n-1)*n
//n!的计算式的递推形式:(n-1)!*n
//令F(n)表示n!,则递归式:F(n) = F(n-1) * n;
//递归边界:F(0)=1(因为0! = 1)
//定义返回值为int型的F(int n)函数,用于递归计算n!
int F(int n){
//判断n是否等于0
if(n == 0){//到达递归边界F(0)时
return 1;//返回1
}else{//没有到达递归边界F(0)时
return F(n-1) * n;//使用递归式递归下去
}
}
int main(){
//n的范围为:n<=16
//当n为大于16的整数时,计算n!会产生溢出,
//如17!已经超出了int型的数据范围
int n;//定义整型变量n
scanf("%d", &n);//输入n
printf("%d\n", F(n));//输出递归计算n!后的结果
return 0;
}
运行结果如下:
2.求Fibonacci数列的第n项
代码如下:
#include<cstdio>
using namespace std;
//递归式:F(n) = F(n-1) + F(n-2)
//递归边界:F(0) = 1; F(1) = 1
//定义返回值为int型的F(int n)函数,用于递归计算数列的第n项
int F(int n){
//判断n是否等于0或n是否等于1
if((n == 0) || (n == 1)){//到达递归边界F(0)或F(1)时
return 1;//返回1
}else{//到达递归边界F(0)或F(1)时
return F(n-1) + F(n-2);//使用递归式递归下去
}
}
int main(){
//n的范围:n>=0,n可以理解为数组的下标
int n;//定义整型变量n,表示数列的第n项
scanf("%d", &n);//输入n
printf("%d\n", F(n));//输出数列的第n项的值
return 0;
}
运行结果如下:
3.全排列
代码如下:
#include<cstdio>
using namespace std;
const int maxn = 11;//maxn表示数组P和hashTable的长度
int n;//n表示1到n的全排列
int P[maxn];//数组P用于存储当前排列
//数组hashTable用于记录整数x在P[0]到P[index-1]中是否出现过
bool hashTable[maxn] = {false};//默认1到n的所有整数均未出现过
//处理当前排列的第index号位
void generateP(int index){
if(index == (n + 1)){//到达递归边界index=(n+1)时
//通过循环输出当前排列
for(int i = 1; i <= n; i++){
printf("%d", P[i]);
}
printf("\n");//输出换行
return;
}
//通过循环枚举1到n,将整数x填入P[index]
for(int x = 1; x <= n; x++){//每趟循环可表示当前排列的第一个数字为x
if(hashTable[x] == false){//x不在P[0]到P[index-1]中
P[index] = x;//将x存储至P[index]中
hashTable[x] = true;//记录x已经在数组P中
generateP(index+1);//递归处理当前排列的第(index+1)号位
hashTable[x] = false;//消除x在数组P中的记录,表示已经处理完P[index]为x的子问题
}
}
}
int main(){
n = 3;//求1到3的全排列
generateP(1);//处理当前排列的第1号位
return 0;
}
运行结果如下:
4.n皇后问题
(1)暴力法
代码如下:
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 11;//maxn表示数组P和hashTable的长度
int n;//n表示在棋盘上放置n个皇后
int P[maxn];//数组P用于存储当前排列
//数组hashTable用于记录整数x在P[0]到P[index-1]中是否出现过
bool hashTable[maxn] = {false};//默认1到n的所有整数均未出现过
int count = 0;//count表示合法的排列方案数
//处理当前排列的第index号位
void generateP(int index){
if(index == (n + 1)){//到达递归边界index=(n+1)时
bool 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){//当前排列为合法排列时
count++;//合法的排列方案数自增1
}
return;
}
//通过循环枚举1到n,将整数x填入P[index]
for(int x = 1; x <= n; x++){//每趟循环可表示当前排列的第一个数字为x
if(hashTable[x] == false){//x不在P[0]到P[index-1]中
P[index] = x;//将x存储至P[index]中
hashTable[x] = true;//记录x已经在数组P中
generateP(index+1);//递归处理当前排列的第(index+1)号位
hashTable[x] = false;//消除x在数组P中的记录,表示已经处理完P[index]为x的子问题
}
}
}
int main(){
n = 8;//在棋盘上放置8个皇后
generateP(1);//处理当前排列的第1号位
printf("放置%d个皇后的合法方案数为:%d\n", n, count);//输出合法方案数
return 0;
}
运行结果如下:
(2)回溯法
代码如下:
#include<cstdio>
#include<cmath>
using namespace std;
const int maxn = 11;//maxn表示数组P和hashTable的长度
int n;//n表示在棋盘上放置n个皇后
int P[maxn];//数组P用于存储当前排列
//数组hashTable用于记录整数x在P[0]到P[index-1]中是否出现过
bool hashTable[maxn] = {false};//默认1到n的所有整数均未出现过
int count = 0;//count表示合法的排列方案数
//处理当前排列的第index号位
void generateP(int index){
if(index == (n + 1)){//到达递归边界index=(n+1)时
//能够到达递归边界的当前排列一定是合法的
count++;//合法的排列方案数自增1
return;
}
//通过循环枚举1到n,将整数x填入P[index]
for(int x = 1; x <= n; x++){//每趟循环可表示当前排列的第一个数字为x
if(hashTable[x] == false){//x不在P[0]到P[index-1]中
bool flag = true;//flag表示当前皇后是否会和之前的皇后冲突,默认为不会冲突
//通过循环遍历之前的皇后来判断当前皇后是否与之前皇后有冲突
for(int pre = 1; pre < index; pre++){
//第index列皇后的行号为x;第pre列皇后的行号为P[pre}
if(abs(index - pre) == abs(x - P[pre])){//当前皇后与之前的某一皇后在同一对角线上
flag = false;//当前皇后与之前皇后存在冲突
break;//退出循环
}
}
if(flag){//当前皇后与之前的皇后不存在冲突时
P[index] = x;//将x存储至P[index]中
hashTable[x] = true;//记录x已经在数组P中
generateP(index+1);//递归处理当前排列的第(index+1)号位
hashTable[x] = false;//消除x在数组P中的记录,表示已经处理完P[index]为x的子问题
}
}
}
}
int main(){
n = 8;//在棋盘上放置8个皇后
generateP(1);//处理当前排列的第1号位
printf("放置%d个皇后的合法方案数为:%d\n", n, count);//输出合法方案数
return 0;
}
运行结果如下:
参考资料如下:
[1] 胡凡,曾磊.算法笔记[M].北京:机械工业出版社,2022:85-98.
[2] codeup网址:http://codeup.hustoj.com
[3] PAT网址:https://www.patest.cn/practice