数据结构之汉诺塔问题深入分析

上次我们学习了汉诺塔问题的代码,但是递归问题理解较难,所以这篇文章来进一步分析汉诺塔问题的代码

先看代码

这一次在原先的代码中加入了一些打印地址的语句,更方便理解程序的运行

#include <stdio.h>

/**
 * Hanoi.
 */
void hanoi(int paraN, char paraSource, char paraDestination, char paraTransit) {
	if (paraN == 1) {
		printf("从%c移动1个盘到%c,%c的地址为%d,%c的地址为%d,能直接移动:\r\n\t%c->%c\r\n", paraSource, paraDestination, paraSource, &paraSource, paraDestination, &paraDestination, paraSource, paraDestination);
		return;
	} else {
		printf("从%c移动%d个盘到%c的初始移动,%c的地址为%d,%c的地址为%d,不能直接移动,需调用hanoi函数先移动%d个盘到%c,过渡地址为%d:\r\n", paraSource, paraN, paraDestination, paraSource, &paraSource, paraDestination, &paraDestination, paraN - 1, paraTransit, &paraTransit);
		hanoi(paraN - 1, paraSource, paraTransit, paraDestination);

		printf("从%c移动1个盘到%c,%c的地址为%d,%c的地址为%d,能直接移动:\r\n\t%c->%c\r\n", paraSource, paraDestination, paraSource, &paraSource, paraDestination, &paraDestination, paraSource, paraDestination);

		printf("从%c移动%d个盘到%c的最终移动,%c的地址为%d,%c的地址为%d,不能直接移动,需调用hanoi函数先移动%d个盘到%c,过渡地址为%d:\r\n", paraSource, paraN, paraDestination, paraSource, &paraSource, paraDestination, &paraDestination, paraN - 1, paraTransit, &paraTransit);
		hanoi(paraN - 1, paraTransit, paraDestination, paraSource);
	}// Of if
}// Of hanoi

/**
 * Test the hanoi function.
 */
void hanoiTest() {
	printf("--- addToTest begins. ---\r\n");

	printf("3 plates\r\n");
	hanoi(3, 'A', 'B', 'C');

	//printf("4 plate\r\n");
	//hanoi(4, 'A', 'B', 'C');

	printf("--- addToTest ends. ---\r\n");
}// Of addToTest

/**
 * The entrance.
 */
void main() {
	hanoiTest();
}// Of main

下面是运行结果

在这里插入图片描述

分析

  1. 自顶向下,逐渐求精
    通过刚才代码的书写,可以看出汉诺塔问题是从大问题逐步分解成小问题来解决的,这就体现了在开发过程中“自顶向下,逐渐求精”的思想。这种思想的好处在于,即使程序在运行中可能需要解决很多小问题,但我们在编写程序时只需要关注大问题即可,对于解决一些复杂问题有所帮助。
  2. 递归与分治
    递归可以看作是一种特殊的循环,通常用于解决能拆分成相同类型的小问题的问题(也就是问题的分治),因为通过调用递归可以很方便地重复解决一种问题,能够减少很多代码的书写。
  3. 不要跨层分析
    我们很难去理解递归之中的每一步都做了什么,因为在每一次调用函数的时候它的参数都可能会发生改变,所以我们只需要把问题分析好,写出最基本的递归式并相信计算机可以处理好多次循环即可。
  4. 形参与实参
    正如上面所说,在每一次调用函数的时候它的参数都可能会发生改变,所以形参和实参就更难以区分。如果真的想了解清楚形参和实参在函数中到底是什么,可以借用编译器的调试和监控功能来分析。
  5. 有意义、规范的标识符
    为了让代码更具可读性,并且方便调试和查看结果,建议使用有实际意义、和问题相关的标识符为变量和函数取名。
  6. 时间复杂度
    汉诺塔问题的时间复杂度为O(2ⁿ),由于T(n) = 2T(n - 1) + 1,T(1) = 1,可以推出:T(n) = 2ⁿ − 1。
  7. 空间复杂度
    汉诺塔问题实际上是用栈来实现的,而实际上栈的空间跟盘子的个数有关,所以空间复杂度为O(n),详情见下面的分析。
  8. 递归栈
    对于内存的分析,就是为了能直观地看出递归在操作系统里面到底是如何进行的。
    在图中我们看出,对于每个需要调用递归的大问题,在初始时和结束时所在的地址并未发生变化,而由它拆分成的小问题则存储于大问题之上并先于它运行完毕。
    所以,系统在运行程序时,先存进去的问题最后才进行运行,这种结构和栈的特点一致,称之为递归栈。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值