汉诺塔递归实现和循环实现的比较

19 篇文章 0 订阅
本文介绍了C语言实现汉诺塔问题的两种解法,包括递归和循环实现。详细展示了代码实现过程,递归解法通过终止条件和递归调用来移动盘子,循环解法则利用二维数组模拟柱子状态,遵循特定移动规则。通过这两个方法,读者可以深入理解汉诺塔问题的解决策略。
摘要由CSDN通过智能技术生成

下面是参考文献

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);
		}
	}
}

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

weixin_39410618

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值