汉诺塔与二叉树

来源:互联网 发布:android 网络抓包工具 编辑:程序博客网 时间:2024/04/24 13:04

我相信很多朋友都知道汉诺塔问题,也有不少看过了它的程序实现,但我想有不少人不懂它是什么意思,为什么那几行程序就把汉诺塔问题给解了呢?

  先啰嗦一会儿。解释一下。。。

看看汉诺塔的由来  (从百度百科中摘来的)

汉诺塔是源自印度神话里的玩具。

 

上帝创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上安大小顺序摞着64片黄金圆盘。

上帝命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。

  有预言说,这件事完成时宇宙会在一瞬间闪电式毁灭。也有人相信婆罗门至今还在一刻不停地搬动着圆盘。

汉诺塔与宇宙寿命  如果移动一个圆盘需要1秒钟的话,等到64个圆盘全部重新落在一起,宇宙被毁灭是什么时候呢?

  让我们来考虑一下64个圆盘重新摞好需要移动多少次吧。1个的时候当然是1次,2个的时候是3次,3个的时候就用了7次......这实在是太累了

  因此让我们逻辑性的思考一下吧。

  4个的时候能够移动最大的4盘时如图所示。

  到此为止用了7次。

  接下来如下图时用1次,在上面再放上3个圆盘时还要用7次(把3个圆盘重新放在一起需要的次数)。

  因此,4个的时候是

  “3个圆盘重新摞在一起的次数”+1次+“3个圆盘重新摞在一起需要的次数”

  =2x“3个圆盘重新摞在一起的次数”+1次

  =15次。

  那么,n个的时候是

  2x“(n-1)个圆盘重新摞在一起的次数”+1次。

  由于1 个的时候是1次,结果n个的时候为(2的n次方减1)次。

  1个圆盘的时候 2的1次方减1

  2个圆盘的时候 2的2次方减1

  3个圆盘的时候 2的3次方减1

  4个圆盘的时候 2的4次方减1

  5个圆盘的时候 2的5次方减1

  ........

  n个圆盘的时候 2的n次方减1

  也就是说,n=64的时候是(2的64次方减1)次。

  因此,如果移动一个圆盘需要1秒的话,

  宇宙的寿命=2的64次方减1(秒)

  2的64次方减1到底有多大呢?动动计算器,答案是一个二十位的数字:

  18446744073709551615

用一年=60秒x60分x24小时x365天来算的话,大约有5800亿年吧。

  据说,现在的宇宙年龄大约是150亿年,还差得远呢。

  汉诺塔问题在数学界有很高的研究价值,

  而且至今还在被一些数学家们所研究,

  也是我们所喜欢玩的一种益智游戏,

  它可以帮助开发智力,激发我们的思维。

 

汉诺塔的C语言实现

  #include<stdio.h>

  void move(char x,char y)

  {

  printf("%c-->%c/n",x,y);

  }

  void hanoi(int n,char one ,char two,char three)

  {

  if(n==1) move(one,three);

  else

  {

  hanoi(n-1,one,three,two);

  move(one,three);

  hanoi(n-1,two,one,three);

  }

  }

  main()

  {

  int m;

  printf("input the number of disks:");

  scanf("%d",&m);

  printf("the step to moving %3d diskes:/n",m);

  hanoi(m,'A','B','C');

  }

 

看到这个程序 ,大家最难理解的就是下面两句: 

  hanoi(n-1,one,three,two);

  hanoi(n-1,two,one,three);

 

我们知道它用了递归,第一句把第一个位置的盘移到第二个位置,第二句再把第二个移到第三个位置。 但为什么这样就可以递归出答案呢?

这个可以用二叉树的中序遍历来解释。下面看一个图估计你就会知道了。

这是一个递归的图解,根结点是第一次进入hanoi函数,第一个参数3只要大于1就往下推,第二个参数是指要移动的盘的原始位置(最上面的那个),第四个参数是指要移动到的目的位置,第三个参数在函数里面没什么用,只是为了下一次递归时准备的一个参数。这个图是按照程序画出来的,上面的节点个数就是调用hanoi函数的次数。现在我们来中序遍历这棵二叉树。

假设:开始时a,b,c三个盘都放在one上,从a到c依次增大,就是说c放在最下面,a在最上面,->表示移动。

节点4:a->three

节点2:b->two

节点5:a->two

节点1:c->three

节点6:a->one

节点3:b->three

节点7:a->three

完成。。。

假设有4个盘呢?那也是一样的道理可以推出。这时就有15个节点,就是说要移动15次。那有n个盘呢?2的n次方减1……

从这边可以看出,递归和二叉树有很多联系。从递归转化成非递归时,一般都可以用二叉树实现。怎么实现呢,可以看一下我的下一篇日志。。。