Crockford的河内功能(来自“The Good Parts”)

时间:2010-09-18 15:46:33

标签: javascript recursion towers-of-hanoi

目前我正在阅读道格拉斯·克罗克福德(Douglas Crockford)的书,而且河内功能的塔楼有点过头了。即使将日志记录到控制台,我也无法真正了解正在发生的事情。这是我添加的功能:

var hanoi = function (disc, src, aux, dst) {
  console.log(disc);
  console.log(src, dst);    
  if (disc > 0) {
    hanoi(disc - 1, src, dst, aux);
    console.log('Move disc ' + disc + ' from ' + src + ' to ' + dst);
    hanoi(disc - 1, aux, src, dst);
  }
}

hanoi(3, 'Src', 'Aux', 'Dst');

这导致以下结果:

  

3
  Src Dst
  2
  Src Aux
  1
  Src Dst
  0
  Src Aux
  将光盘1从Src移动到Dst
  0
  Aux Dst
  将光盘2从Src移动到Aux
  1
  Dst Aux
  0
  Dst Src
  将光盘1从Dst移动到Aux
  0
  Src Aux
  将光盘3从Src移动到Dst
  2
  Aux Dst
  1
  Aux Src
  0
  Aux Dst
  将光盘1从Aux移动到Src
  0
  Dst Src
  将光盘2从Aux移动到Dst
  1
  Src Dst
  0
  Src Aux
  将光盘1从Src移动到Dst
  0
  Aux Dst

我早早就输了。在结果的第6行,它怎么能从Src Aux回到Src Dst?

当该功能仅使用“disc-1”调用自身时,如果光盘数量达到0,它又如何能够再次上升?

2 个答案:

答案 0 :(得分:8)

产生混淆是因为输出的文本表示不是理解递归的最佳方式。光盘数量没有“上升”,而是hanoi(1)hanoi(0)完成后继续运行。

我在JSBin创建了一个修改过的示例,它以一种(有些)更漂亮的方式用空格打印输出。只有“移动”实际上做了什么,剩下的行只是递归调用来解决较小的子问题,以便以后解决整个问题。

您可能还想查看此Java applet,以图形方式显示算法的工作原理 - 这可能更容易理解。

答案 1 :(得分:5)

河内之塔是递归可以简化特定问题的一个很好的例子。这个想法如下:您必须将N个磁盘从源堆栈移动到目标堆栈,一次一个磁盘,并且永远不能将较大的磁盘放在较小的磁盘上。您可以使用辅助堆栈。假设N = 10.你不知道如何解决它。但是你可以让问题更简单(你希望):

move 9 disks to the auxiliary stack,  
move the remaining (and largest!) disk to the destination stack, and  
move the 9 disks from the auxiliary stack to the destination stack  

同样,您不知道如何移动9磁盘堆栈,但这也没有问题:

move 8 disks from the auxiliary stack to the source stack,  
move the remaining disk to the destination stack (there are 2 disks now), and  
move the 8 disks from the source stack to the destination stack  

重复此操作,直到您必须移动的堆栈只有1个磁盘大。

关于再次上升的磁盘数:为N-1磁盘递归调用该函数,在函数中将其分配给N.此N仅在函数结束时存在,并返回到上一级。然后你再次得到N的旧值。