下面是参考文献
c语言程序设计 夏涛 主编 北京邮电大学出版社
https://blog.csdn.net/maomao8246/article/details/84841431
下面代码完全参考教科书
// hanoi.c 汉诺塔的递归实现
#include <stdio.h>
void move(unsigned int n, char* head, char* tail);
void hanoi(unsigned int n, char* a, char* b, char* c);
int main(void) {
hanoi(3, "A", "B", "C");
}
// 仅仅是显示盘子移动的某一步。
void move(unsigned int n, char* head, char* tail) {
printf(" %d: %s -> %s\n", n, head, tail);
}
void hanoi(unsigned int n, char* a, char* b, char* c) {
if(n == 1) { // 递归的终止条件
move(n, a, c);
} else {
// 递归过程,如果把除了最大盘子的所有盘子移动从A到B上
hanoi(n - 1, a, c, b);
move(n, a, c); // 然后再把最大的盘子移动到C上
// 最后如果把除了最大盘子的所有盘子移动从B到C上
hanoi(n - 1, b, a, c); // 则完成了所有盘子的移动
}
}
循环实现并不是仅此一条思路,但是这个更适合我的口味,下面代码大量参考【SECE_毛毛】的原创文章
// hanoi1.c 汉诺塔的循环实现
#include <stdio.h>
// 初始化汉诺塔,即是把盘子按顺序摆放在A柱子上。
void initHanoi(int (*a)[3], int total);
// 在柱子上(二维数组中)找到1个盘子。
int* find(int* loc, int (*a)[3], int which, int total);
// 打印移动过程
void printMove(int* loc, int (*a)[3], int nowRow, int nowCol);
// 移动盘子,即是按照单左双右、先小后大、一步二步法则移动。
void move(int* loc, int (*a)[3], int total);
// 一旦确定了移动方向,就按此方向循环先小后大、一步二步法则。
void hanoi(int* loc, int (*a)[3], int total);
int main(void) {
int total, i;
total = 3;
int loc[2] = {0, 0};
int a[total][3];
initHanoi(a, total);
hanoi(loc, a, total);
}
void initHanoi(int (*a)[3], int total) {
int row;
// 初始化过程通过遍历二维数组,将柱子1的盘子摆放好,其他位置则清零。
for(row = 0; row < total; row++) {
a[row][0] = row + 1;
a[row][1] = 0;
a[row][2] = 0;
}
}
int* find(int* loc, int (*a)[3], int which, int total) {
int row, col;
// 查找的过程仍然是遍历,按照盘子序号查找,返回二维坐标。
for(col = 0; col < 3; col++) {
for(row = 0; row < total; row++) {
loc[0] = row;
loc[1] = col;
if(a[row][col] == which) {
return loc;
}
}
}
}
void printMove(int* loc, int (*a)[3], int nowRow, int nowCol) {
char from, to;
// loc[1]为移动前的柱子。
switch(loc[1]) {
case 0: from = 'A'; break;
case 1: from = 'B'; break;
case 2: from = 'C'; break;
default:
break;
}
// nowCol为移动后的柱子。
switch(nowCol) {
case 0: to = 'A'; break;
case 1: to = 'B'; break;
case 2: to = 'C'; break;
default:
break;
}
// a[nowRow][nowCol]是被移动盘子的序号
printf("%d: %c -> %c\n",a[nowRow][nowCol], from, to);
}
void move(int* loc, int (*a)[3], int total) {
int nowRow, nowCol, nextRow, nextCol, i, t, judge;
nowRow = loc[0];
nowCol = loc[1];
t = a[nowRow][nowCol];
if(total % 2 == 0) {
judge = 1;
} else {
judge = -1;
}
nextCol = loc[1] + judge;
// 先小后大、一步二步法则移动。
for(i = 0; i < 2; i++) { // 这里就是某个盘子可能移动的两步。
// 对数组的柱子维的越界进行处理,只有A,B,C三个柱子。
if(nextCol < 0) {
nextCol = 2;
}
if(nextCol > 2) {
nextCol = 0;
}
// 逆序遍历二维数组,尝试找到1个符合要求的放置可能被盘子的位置。
for(nextRow = total - 1; nextRow >= 0; nextRow--) {
// 处理第一个盘子越界问题。
if(nowRow != 0) {
// 如果要移动的盘子的上面存在其他盘子则不可移动。
if(a[nowRow - 1][nowCol] > 0) {
return;
}
}
// 如果在下一个柱子上找到一个盘子,则需要判断该盘子是否比要移动的盘子大,
// 大则初步判断可以移动。
if(a[nextRow][nextCol] > a[nowRow][nowCol]) {
// 如果可以移动,就判断该盘子的上面是否为空。
if(a[nextRow - 1][nextCol] == 0) {
// 移动盘子是把之前的位置置空,把现在的位置写入盘子号
a[nowRow][nowCol] = 0;
nowRow = nextRow - 1;
nowCol = nextCol;
a[nowRow][nowCol] = t;
printMove(loc, a, nowRow, nowCol);
return;
}
}
// 特殊情况是,要移动的柱子上根本没有盘子,所以直接移动。
if(a[total - 1][nextCol] == 0) {
a[nowRow][nowCol] = 0;
nowRow = nextRow;
nowCol = nextCol;
a[nowRow][nowCol] = t;
printMove(loc, a, nowRow, nowCol);
return;
}
}
// 按照某一方向,这是要进行某个盘子的第二步移动(移动需要符合规则)。
// 请考虑汉诺塔的移动规则。
nextCol += judge;
}
}
void hanoi(int* loc, int (*a)[3], int total) {
int i;
// 第一个盘子最终移动到C柱子上的判定条件
while(a[0][2] != 1) {
// 正序遍历每一个盘子,并按照先小后大、一步二步法则移动。
for(i = 1; i <= total; i++) {
find(loc, a, i, total);
// 先小后大、一步二步法则的移动由下面函数完成。
move(loc, a, total);
}
}
}