汉诺塔非递归算法

这两天讲《Web程序设计》,用JavaScript写了个汉诺塔的非递归算法,觉得有点意思,放在这里吧!

传统的递归算法: 

 
 
  1. <html xmlns="http://www.w3.org/1999/xhtml"> 
  2. <head> 
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  4. <title>Hanoi Tower (Recursive algorithm) - 汉诺塔(递归算法)</title> 
  5. <style type="text/css"> 
  6. i {  
  7.     color: #0000ff;  
  8. }  
  9. P {  
  10.     text-align: center;  
  11. }  
  12. </style> 
  13. <script type="text/javascript"> 
  14. var step=0;  
  15. function MoveOnePlate(n, loca1, loca2)  
  16. {  
  17.     document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");  
  18. }  
  19.  
  20. function MovePlates(n, s, m, d)  
  21. {  
  22.     if(n==1)  
  23.       MoveOnePlate(n, s, d);  
  24.     else  
  25.     {  
  26.         MovePlates(n-1, s, d, m);  
  27.         MoveOnePlate(n, s, d);  
  28.         MovePlates(n-1, m, s, d);  
  29.     }  
  30. }  
  31. </script> 
  32. </head> 
  33.  
  34. <body> 
  35. <p>Hanoi Tower (Recursive algorithm) - 汉诺塔(递归算法)<br />Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br /> 
  36. Copyright 2011, All right reserved. - 版权所有(C) 2011<br />2011.03.31</p> 
  37. <script type="text/javascript"> 
  38.     n=parseInt(prompt("请输入盘子的数量:", 3), 10);  
  39.     if (isNaN(n) || n<1 || n>16)  
  40.     {  
  41.         alert("请输入介于1到16的自然数!");  
  42.         location.reload();  
  43.     }  
  44.     else  
  45.     {  
  46.         document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n)-1) + "步:<br /><br />");  
  47.         MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");  
  48.     }  
  49. </script> 
  50. </body> 
  51. </html> 

这个递归算法就不详细说了,核心代码只有5、6行。
下面是非递归算法: 

 
 
  1. <html xmlns="http://www.w3.org/1999/xhtml"> 
  2. <head> 
  3. <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  4. <title>Hanoi Tower (Non-recursive algorithm) - 汉诺塔(非递归算法)</title> 
  5. <style type="text/css"> 
  6. i {  
  7.     color: #ff0000;  
  8. }  
  9. p {  
  10.     text-align: center;  
  11. }  
  12. </style> 
  13. <!--  
  14. 汉诺塔非递归算法:  
  15. (1)、若问题规模n为偶数,按顺时针方向依次摆放s, m, d为环,若n为奇数,则为s, d, m;  
  16. (2)、将盘子由小到大逐个编号并放置在s上,即最小的盘子编号为1,放在最上面;  
  17. (3)、按顺时针方向把编号为1的盘子从当前的位置移动到下一位置;  
  18. (4)、把另外两个位置(即除去1号盘子所在的位置)中非空的那个上的一个圆盘移到空的位置上,如果另外两个位置都非空,则移动编号较小的那一个;  
  19. (5)、重复进行(3)和(4),直到移动的次数等于2^n-1。  
  20. --> 
  21. <script type="text/javascript"> 
  22. var step=0;  
  23. function MoveOnePlate(n, loca1, loca2)  
  24. {  
  25.     document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");  
  26. }  
  27.  
  28. function MovePlates(n, s, m, d)  
  29. {  
  30.     //定义位置  
  31.     var loop=new Array(3);  
  32.     loop[0]=new Array(n);  
  33.     loop[1]=new Array(n);  
  34.     loop[2]=new Array(n);  
  35.     //定义位置描述字符串数组(n为偶数的情况)  
  36.     var loca=new Array(s, m, d);  
  37.     if (n%2!=0) //n为奇数的情况  
  38.     {  
  39.         loca[1]=d;  
  40.         loca[2]=m;  
  41.     }  
  42.     //初始化源位置上的盘子  
  43.     for(var i=0; i<n; i++)  
  44.         loop[0][i]=n-i;  
  45.     //记录各个位置上盘子的数量  
  46.     var loopLen=new Array(n, 0, 0);  
  47.     var count=Math.pow(2, n)-1; //移动次数,即循环退出条件  
  48.     var firstPlate=0; //1号盘子的位置  
  49.     do  
  50.     {  
  51.         //将1号盘子顺时针移动到后1个位置  
  52.         MoveOnePlate(1, loca[firstPlate], loca[(firstPlate+1)%3]); //显示移动过程  
  53.         loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]]=1; //移动  
  54.         loopLen[firstPlate]--; //修改1号盘子旧位置上盘子的数量  
  55.         firstPlate=(firstPlate+1)%3; //修改1号盘子的位置  
  56.         loopLen[firstPlate]++; //修改1号盘子新位置上盘子的数量  
  57.         count--; //记录移动次数  
  58.         //移动另外的两个位置上的盘子  
  59.         if(count!=0) //避免最后一次移动后仍然移动而导致错误  
  60.         {  
  61.             //确定另外两个位置如何移动  
  62.             if (loopLen[(firstPlate+1)%3]==0 || loopLen[(firstPlate+2)%3]!=0 &&  
  63.                 loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1] < loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1] )  
  64.             { //1号盘子的后第1个位置为空,或者无空位置且1号盘子后第2个位置编号较小,此时将1号盘子后第2个位置的盘子移动到1号盘子后第1个位置上  
  65.                 MoveOnePlate(loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1], loca[(firstPlate+2)%3], loca[(firstPlate+1)%3]); //显示移动过程  
  66.                 loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]]=loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1]; //移动  
  67.                 loopLen[(firstPlate+2)%3]--; //修改该盘子旧位置上盘子的数量  
  68.                 loopLen[(firstPlate+1)%3]++; //修改该盘子新位置上盘子的数量  
  69.             }  
  70.             else  
  71.             { //1号盘子的后第2个位置为空,或者无空位置且1号盘子后第1个位置编号较小,此时将1号盘子后第1个位置的盘子移动到1号盘子后第2个位置上  
  72.                 MoveOnePlate(loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1], loca[(firstPlate+1)%3], loca[(firstPlate+2)%3]); //显示移动过程  
  73.                 loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]]=loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1]; //移动  
  74.                 loopLen[(firstPlate+1)%3]--; //修改该盘子旧位置上盘子的数量  
  75.                 loopLen[(firstPlate+2)%3]++; //修改该盘子新位置上盘子的数量  
  76.             }  
  77.             count--; //记录移动次数  
  78.         }  
  79.     } while(count!=0)  
  80. }  
  81. </script> 
  82. </head> 
  83.  
  84. <body> 
  85. <p>Hanoi Tower (Non-recursive algorithm) - 汉诺塔(非递归算法)<br />Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br /> 
  86. Copyright 2011, All right reserved. - 版权所有(C) 2011<br />2011.03.31</p> 
  87. <script type="text/javascript"> 
  88.     n=parseInt(prompt("请输入盘子的数量:", 3), 10);  
  89.     if (isNaN(n) || n<1 || n>16)  
  90.     {  
  91.         alert("请输入介于1到16的自然数!");  
  92.         location.reload();  
  93.     }  
  94.     else  
  95.     {  
  96.         document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n)-1) + "步:<br /><br />");  
  97.         MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");  
  98.     }  
  99. </script> 
  100. </body> 
  101. </html> 

非递归算法略微复杂些,代码里面有详细的注释和说明。

这里还有一个使用JavaScript数组对象的push()和pop()方法的非递归版本,可以简化程序,道理是一样的。
非递归算法版本2: 

 
 
  1. <html xmlns="http://www.w3.org/1999/xhtml"> 
  2. <head> 
  3.     <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> 
  4.     <title>Hanoi Tower (Non-recursive algorithm, Version 2) - 汉诺塔(非递归算法,版本2)</title> 
  5.     <style type="text/css"> 
  6.         i  
  7.         {  
  8.             color: #ff0080;  
  9.         }  
  10.         p  
  11.         {  
  12.             text-align: center;  
  13.         }  
  14.     </style> 
  15.     <!--  
  16. 汉诺塔非递归算法:  
  17. (1)、若问题规模n为偶数,按顺时针方向依次摆放s, m, d为环,若n为奇数,则为s, d, m;  
  18. (2)、将盘子由小到大逐个编号并放置在s上,即最小的盘子编号为1,放在最上面;  
  19. (3)、按顺时针方向把编号为1的盘子从当前的位置移动到下一位置;  
  20. (4)、把另外两个位置(即除去1号盘子所在的位置)中非空的那个上的一个圆盘移到空的位置上,如果另外两个位置都非空,则移动编号较小的那一个;  
  21. (5)、重复进行(3)和(4),直到移动的次数等于2^n-1。  
  22. --> 
  23.     <script type="text/javascript"> 
  24.         var step = 0;  
  25.         function MoveOnePlate(n, loca1, loca2) {  
  26.             document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");  
  27.         }  
  28.  
  29.         function MovePlates(n, s, m, d) {  
  30.             //定义位置  
  31.             var loop = new Array([], [], []);  
  32.             //定义位置描述字符串数组(n为偶数的情况)  
  33.             var loca = new Array(s, m, d);  
  34.             if (n % 2 != 0) //n为奇数的情况  
  35.             {  
  36.                 loca[1] = d;  
  37.                 loca[2] = m;  
  38.             }  
  39.             //初始化源位置上的盘子  
  40.             for (var i = 0; i < n; i++)  
  41.                 loop[0].push(n - i);  
  42.             var count = Math.pow(2, n) - 1; //移动次数,即循环退出条件  
  43.             var firstPlate = 0; //1号盘子的位置  
  44.             do {  
  45.                 //将1号盘子顺时针移动到后1个位置  
  46.                 MoveOnePlate(1, loca[firstPlate], loca[(firstPlate + 1) % 3]); //显示移动过程  
  47.                 loop[(firstPlate + 1) % 3].push(loop[firstPlate].pop()); //从旧位置移动到新位置  
  48.                 firstPlate = (firstPlate + 1) % 3; //修改1号盘子的位置  
  49.                 count--; //记录移动次数  
  50.                 //移动另外的两个位置上的盘子  
  51.                 if (count != 0) //避免最后一次移动后仍然移动而导致错误  
  52.                 {  
  53.                     //确定另外两个位置如何移动  
  54.                     if (loop[(firstPlate + 1) % 3].length == 0 || loop[(firstPlate + 2) % 3].length != 0 && loop[(firstPlate + 2) % 3][loop[(firstPlate + 2) % 3].length - 1] < loop[(firstPlate + 1) % 3][loop[(firstPlate + 1) % 3].length - 1]) {  
  55.                         //1号盘子的后第1个位置为空,或者无空位置且1号盘子后第2个位置编号较小,此时将1号盘子后第2个位置的盘子移动到1号盘子后第1个位置上  
  56.                         MoveOnePlate(loop[(firstPlate + 2) % 3][loop[(firstPlate + 2) % 3].length - 1], loca[(firstPlate + 2) % 3], loca[(firstPlate + 1) % 3]); //显示移动过程  
  57.                         loop[(firstPlate + 1) % 3].push(loop[(firstPlate + 2) % 3].pop()); //从旧位置移动到新位置  
  58.                     }  
  59.                     else {  
  60.                         //1号盘子的后第2个位置为空,或者无空位置且1号盘子后第1个位置编号较小,此时将1号盘子后第1个位置的盘子移动到1号盘子后第2个位置上  
  61.                         MoveOnePlate(loop[(firstPlate + 1) % 3][loop[(firstPlate + 1) % 3].length - 1], loca[(firstPlate + 1) % 3], loca[(firstPlate + 2) % 3]); //显示移动过程  
  62.                         loop[(firstPlate + 2) % 3].push(loop[(firstPlate + 1) % 3].pop()); //从旧位置移动到新位置  
  63.                     }  
  64.                     count--; //记录移动次数  
  65.                 }  
  66.             } while (count != 0)  
  67.         }  
  68.     </script> 
  69. </head> 
  70. <body> 
  71.     <p> 
  72.         Hanoi Tower (Non-recursive algorithm, Version 2) - 汉诺塔(非递归算法,版本2)<br /> 
  73.         Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br /> 
  74.         Copyright 2011, All right reserved. - 版权所有(C) 2011<br /> 
  75.         2011.04.04</p> 
  76.     <script type="text/javascript"> 
  77.         n = parseInt(prompt("请输入盘子的数量:", 3), 10);  
  78.         if (isNaN(n) || n < 1 || n > 16) {  
  79.             alert("请输入介于1到16的自然数!");  
  80.             location.reload();  
  81.         }  
  82.         else {  
  83.             document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n) - 1) + "步:<br /><br />");  
  84.             MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");  
  85.         }  
  86.     </script> 
  87. </body> 
  88. </html> 

这里是三个算法网页源文件的连接:
http://mengliao.blog.51cto.com/attachment/201104/876134_1301907387.rar

点赞

发表评论

电子邮件地址不会被公开。 必填项已用*标注