汉诺塔问题:
(1)问题描述
1.有三根杆子X,Y,Z,X杆上有n只碟子
2. 每次移动一块碟子,小的只能叠在大的上面
3. 把所有碟子从X杆经Y杆全部移到z杆上。
(2)移动步骤
(下图参考懒猫老师的《数据结构》相关课程)
只要将这三步用函数调用的形式展现,并且以当n=1时,只用将最后一个碟子从Y->Z,即可实现递归求步骤
(3)函数说明
- void hanoi(int n, char x, char y, char z)
参数n--要移动的汉诺塔的层数(从上往下数总共的)
x,y,z---将x上的n个碟子经过y这个中转站,最终全部移动到z上
- void move(char c1, char c2)
打印由c1->c2,表示把X杆上第n个碟子移到Z杆
(4)完整代码
#include <stdio.h>
int main() {
int n;
printf("Please input n:");
scanf("%d", &n);
hanoi(n, 'a', 'b', 'c');
}
void hanoi(int n, char x, char y, char z) {
if (n == 1)//递归函数的出口
move(x, z);
else {
hanoi(n - 1, x, z, y);
move(x, z);
hanoi(n - 1, y, x, z);
}
}
void move(char c1, char c2) {//打印移动情况的函数
printf("%c->%c\n", c1, c2);
}
(5)堆栈逻辑
递归入栈保存的方式是,保存当前函数执行的行数,以及各参数值;将当前的函数执行行入栈后,从函数头行重新执行该函数,直至达到递归函数的出口条件,再逐个将栈中的函数执行x
八皇后问题:
(1)问题描述
在一个8×8的棋盘里放置 8个皇后,要求每个皇后两两之间不相“冲”,即在每一横列,竖列,斜列只有一个皇后, 要求输出所有的皇后排列组合可能,具体可以参考下这个图:
(2)算法思路
(下面是参考懒猫老师的《数据结构》相关课程的笔记,讲的超级清楚!)
- 判断格子是否被占
(循环一行一行的找皇后可以选择的位置,所以行不用加额外的判断)
(1)列 int flag[8],初始化值为0,当该列被占后赋值为1
(2)对角线 上对角线:int d1[15];下对角线:int d2[15];初始化值为0,当该列被占后赋值为1
关于对角线的处理,要保证每条对角线上的每个点的判断值一致,才能一次判断一整条,对于上对角线,采取的是di[行-列+7],加7是因为(行-列)的值域为[-7~7],为使数组可以搜索,就+7为正
对于下对角线,采取的是di[行+列]:
- 回溯算法
概念:回溯法是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先选择并不优或达不到目标,就退回一步重新选择,这种走不通就退回再走的技术为回溯法,而满足回溯条件的某个状态的点称为“回溯点”。
基本实现:
void dfs(参数)
{
if(满足递归终止条件)
{
存放结果
return;
}else{
for(遍历本层集合中的元素)
{
处理结点;
dfs(参数);//递归
撤销处理该结点; //回溯
}
}
}
本题应用:
以图示八皇后其中一个解为例来说明递归过程:(如果不好理解的话可以直接看代码)
(1)退出递归条件:n==7
自相似:寻找第(n+1)个皇后的位置
(2)一直循环递归调用gernerate(n+1),直至寻找到最后一行的皇后,打印当前解,然后回溯寻找7行(以0行为开始计算的)是否有另一种皇后放法(这里的列是从上一次放过的皇后位置后面找),若没有,从栈中弹出上一次函数调用,即gernerate(7),直接执行回溯(这里要注意递归函数出栈时直接执行入栈行的下一个语句),回溯后循环看第6行是否有另一种皇后放法,以此类推;若有,就宣布占领,入栈调用递归函数gernerate(n+1),往后直至n==7,再次打印新的解。
(3)完整代码
#include <stdio.h>
#include <stdlib.h>
int place[8];//下标代表行号,值代表列号,用于输出
int flag[8] = {1, 1, 1, 1, 1, 1, 1, 1}; //列
int d1[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; //上对角线
int d2[15] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; //下对角线
int count = 1;//解的编号
main() {
gernerate(0);
return 0;
}
void gernerate(int n) {
int col;//列号
for (col = 0; col < 8; col++) { //每个皇后都要遍历行找
if (flag[col] != 0 && d1[n - col + 7] != 0 && d2[n + col] != 0) { //该点还未被占领
place[n] = col; //放置皇后,记录输出
flag[col] = 0;//更新占领状态
d1[n - col + 7] = 0;
d2[n + col] = 0;
if (n < 7)
gernerate(n + 1); //入栈
else
display();//执行完这步后,一步步往回回溯
//清除回溯,下面只有在出栈时会执行
flag[col] = 1;//清除本行占领状态
d1[n - col + 7] = 1;
d2[n + col] = 1;
}
}
}
void display() {
printf("第%d组八皇后的解分布图为:\n", count);
count++;
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++) {
if (place[i] == j)
printf("& ");
else
printf(". ");
}
printf("\n");
}
}
输出:(共92组解)
初学小白,有错误欢迎指正喔~!!