汉诺塔的与解析
2016-01-10 14:39
239 查看
学习递归时,汉诺塔问题算是一个经典算法了,当时想了好久没想明白,后来看了几篇文章,找到一篇解释的很清晰的,感谢下作者。
文中代码用的貌似是C#,自己学的是C,所以结合自己的理解,整理了C版本的如下:
首先应明白一些递归的知识,递归进行自身函数的层层调用,一层层的进去,但要记得从原路一层层的返回,从哪进去,执行完调用后还要从哪出来,然后继续该语句后的语句。
下面开始汉诺塔的讲解:
题目:从左到右有 A B C三个柱子, 大盘子在下, 小盘子在上,要求借助B柱将所有盘子从A柱移动到C柱, 期间有一个原则: 大盘子只能在小盘子的下面.
先分析下简单的情况:
案例 1 - 假设只有一个盘子的时候, 盘子数量 N=1
只有一个步骤 将第1个盘子从A移动到C, 为了对比方便我这样来描述这个步骤:
案例 2 - 如果有两个盘子, 盘子数量 N = 2
案例 3 - 如果有三个盘子, 盘子数量 N = 3
如何找出盘子移动的规律 ?
我们要做的最重要的一件事情就是永远要把最底下的一个盘子从 A 移动到 C
看看上面从1个盘子的移动到3个盘子的移动, 在移动记录中,当盘子的编号和盘子数量相同的时候他们的步骤都是从A移动到C (看加粗的部分),其它的步骤对等.
再观察第3个案例中的第1-3 步 和 第 5-7步
第 1-3 步 目的是从 A 移动到 B 如果我们把 B 当作终点, 那么这里的第 1-3 步理解起来和 第2个案例的三个步骤完全相同, 都是通过一个柱子来移动,和第2个案例比起来在后面加括号来表示
总结:将盘子B变成C即可.
第 5-7 步 目的是从 B 移动到 C 如果我们把 C 当作终点, 那么这里的 5-7 步理解起来和上面也是一样的, 和第2个案例的三个步骤也完全相同.和第2个案例比起来就是:
总结: 将盘子B变成A即可
根据这个演示可以明确几点规律:
当盘子只有一个的时候,只有一个动作 从 A 移动到 C 即结束.
当有N个盘子的时候, 中间的动作都是从 A 移动到 C, 那么表示最下面的第N个盘子移动完毕
中间动作之上都可以认为是: 从 A 移动到 B
中间动作之下都可以认为是: 从 B 移动到 C
2,3,4 可以表示为
以下C语言版的代码:
结合上面的分析,最重要的就是这里的3步交换动作, 中间从 A到C的动作是最底层盘子的最终操作.
//Step 1 - A –> B
MoveDisk(DiskQuantity - 1, PositionA,PositionC,PositionB);
//Step 2 - A –> C
MoveDisk(1, PositionA, PositionB, PositionC);
//Step 3 - B –> C
MoveDisk(DiskQuantity - 1, PositionB, PositionA, PositionC);
至于第1个参数为什么是DiskQuantity - 1,或者1 大家再回到上面看看是不是所有的步骤都是.. 1. 1,2,1. 1,2,1,3,1,2,1 这种以盘子数对称的结构,而它前后都是重复1,2,1 的过程.
原文地址:http://www.jcodecraeer.com/a/chengxusheji/shejimoshi/2013/0307/984.htmlal-and-quick-reference
文中代码用的貌似是C#,自己学的是C,所以结合自己的理解,整理了C版本的如下:
首先应明白一些递归的知识,递归进行自身函数的层层调用,一层层的进去,但要记得从原路一层层的返回,从哪进去,执行完调用后还要从哪出来,然后继续该语句后的语句。
下面开始汉诺塔的讲解:
题目:从左到右有 A B C三个柱子, 大盘子在下, 小盘子在上,要求借助B柱将所有盘子从A柱移动到C柱, 期间有一个原则: 大盘子只能在小盘子的下面.
先分析下简单的情况:
案例 1 - 假设只有一个盘子的时候, 盘子数量 N=1
只有一个步骤 将第1个盘子从A移动到C, 为了对比方便我这样来描述这个步骤:
步骤 | 盘子编号 | 从柱子 | 移到柱子 |
---|---|---|---|
1 | 1 | A | C |
步骤 | 盘子编号 | 从柱子 | 移到柱子 |
---|---|---|---|
1 | 1 | A | |
2 | 2 | A | |
3 | 1 | B | C |
步骤 | 盘子编号 | 从柱子 | 移到柱子 |
---|---|---|---|
1 | 1 | A | C |
2 | 2 | A | B |
3 | 1 | C | B |
4 | 3 | A | C |
5 | 1 | B | A |
6 | 2 | B | C |
7 | 1 | A | C |
我们要做的最重要的一件事情就是永远要把最底下的一个盘子从 A 移动到 C
看看上面从1个盘子的移动到3个盘子的移动, 在移动记录中,当盘子的编号和盘子数量相同的时候他们的步骤都是从A移动到C (看加粗的部分),其它的步骤对等.
再观察第3个案例中的第1-3 步 和 第 5-7步
第 1-3 步 目的是从 A 移动到 B 如果我们把 B 当作终点, 那么这里的第 1-3 步理解起来和 第2个案例的三个步骤完全相同, 都是通过一个柱子来移动,和第2个案例比起来在后面加括号来表示
步骤 | 盘子编号 | 从柱子 | 移到柱子 | 备注 |
---|---|---|---|---|
1 | 1 | A | C | ( A -> B) |
2 | 2 | A | B | ( A -> C) |
3 | 1 | C | B | ( B -> C) |
第 5-7 步 目的是从 B 移动到 C 如果我们把 C 当作终点, 那么这里的 5-7 步理解起来和上面也是一样的, 和第2个案例的三个步骤也完全相同.和第2个案例比起来就是:
步骤 | 盘子编号 | 从柱子 | 移到柱子 | 备注 |
---|---|---|---|---|
5 | 1 | B | A | ( A -> B) |
6 | 2 | B | C | ( A- > C) |
7 | 1 | A | C | ( B -> C) |
根据这个演示可以明确几点规律:
当盘子只有一个的时候,只有一个动作 从 A 移动到 C 即结束.
当有N个盘子的时候, 中间的动作都是从 A 移动到 C, 那么表示最下面的第N个盘子移动完毕
中间动作之上都可以认为是: 从 A 移动到 B
中间动作之下都可以认为是: 从 B 移动到 C
2,3,4 可以表示为
步骤 | 盘子编号 | 从柱子 | 移到柱子 |
---|---|---|---|
1 | 1 | A | B |
2 | 2 | A | C |
3 | 1 | B | C |
#include <stdio.h> void MoveDisk(int DiskQuantity,char PositionA,char PositionB,char PositionC) { if(DiskQuantity==1) { printf("Move disk from position %c to %c.\n",PositionA,PositionC); return; } else { // Step 1 - Change B to C (A --> B) MoveDisk(DiskQuantity - 1, PositionA,PositionC,PositionB); // Step 2 - No changes (A --> C) MoveDisk(1, PositionA, PositionB, PositionC); // Step 3 - Change B to A (A --> C) MoveDisk(DiskQuantity - 1, PositionB, PositionA, PositionC); } } void main() { int DiskQuantity; char PositionA,PositionB,PositionC; printf("Please input Disk Quantity:"); scanf("%d",&DiskQuantity); MoveDisk(DiskQuantity,'A','B','C'); }
结合上面的分析,最重要的就是这里的3步交换动作, 中间从 A到C的动作是最底层盘子的最终操作.
//Step 1 - A –> B
MoveDisk(DiskQuantity - 1, PositionA,PositionC,PositionB);
//Step 2 - A –> C
MoveDisk(1, PositionA, PositionB, PositionC);
//Step 3 - B –> C
MoveDisk(DiskQuantity - 1, PositionB, PositionA, PositionC);
至于第1个参数为什么是DiskQuantity - 1,或者1 大家再回到上面看看是不是所有的步骤都是.. 1. 1,2,1. 1,2,1,3,1,2,1 这种以盘子数对称的结构,而它前后都是重复1,2,1 的过程.
原文地址:http://www.jcodecraeer.com/a/chengxusheji/shejimoshi/2013/0307/984.htmlal-and-quick-reference
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- 书评:《算法之美( Algorithms to Live By )》
- 动易2006序列号破解算法公布
- Ruby实现的矩阵连乘算法
- C#插入法排序算法实例分析
- Lua和C语言的交互详解
- 有关数据库SQL递归查询在不同数据库中的实现方法
- 超大数据量存储常用数据库分表分库算法总结
- C#数据结构与算法揭秘二
- C#中的递归APS和CPS模式详解
- C#冒泡法排序算法实例分析
- WinForm实现按名称递归查找控件的方法
- 算法练习之从String.indexOf的模拟实现开始
- C#算法之关于大牛生小牛的问题
- 使用SqlServer CTE递归查询处理树、图和层次结构
- C#实现的算24点游戏算法实例分析
- C#中的尾递归与Continuation详解
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数