这两天讲《Web程序设计》,用JavaScript写了个汉诺塔的非递归算法,觉得有点意思,放在这里吧!
传统的递归算法:
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Hanoi Tower (Recursive algorithm) - 汉诺塔(递归算法)</title>
- <style type="text/css">
- i {
- color: #0000ff;
- }
- P {
- text-align: center;
- }
- </style>
- <script type="text/javascript">
- var step=0;
- function MoveOnePlate(n, loca1, loca2)
- {
- document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");
- }
- function MovePlates(n, s, m, d)
- {
- if(n==1)
- MoveOnePlate(n, s, d);
- else
- {
- MovePlates(n-1, s, d, m);
- MoveOnePlate(n, s, d);
- MovePlates(n-1, m, s, d);
- }
- }
- </script>
- </head>
- <body>
- <p>Hanoi Tower (Recursive algorithm) - 汉诺塔(递归算法)<br />Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br />
- Copyright 2011, All right reserved. - 版权所有(C) 2011<br />2011.03.31</p>
- <script type="text/javascript">
- n=parseInt(prompt("请输入盘子的数量:", 3), 10);
- if (isNaN(n) || n<1 || n>16)
- {
- alert("请输入介于1到16的自然数!");
- location.reload();
- }
- else
- {
- document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n)-1) + "步:<br /><br />");
- MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");
- }
- </script>
- </body>
- </html>
这个递归算法就不详细说了,核心代码只有5、6行。
下面是非递归算法:
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Hanoi Tower (Non-recursive algorithm) - 汉诺塔(非递归算法)</title>
- <style type="text/css">
- i {
- color: #ff0000;
- }
- p {
- text-align: center;
- }
- </style>
- <!--
- 汉诺塔非递归算法:
- (1)、若问题规模n为偶数,按顺时针方向依次摆放s, m, d为环,若n为奇数,则为s, d, m;
- (2)、将盘子由小到大逐个编号并放置在s上,即最小的盘子编号为1,放在最上面;
- (3)、按顺时针方向把编号为1的盘子从当前的位置移动到下一位置;
- (4)、把另外两个位置(即除去1号盘子所在的位置)中非空的那个上的一个圆盘移到空的位置上,如果另外两个位置都非空,则移动编号较小的那一个;
- (5)、重复进行(3)和(4),直到移动的次数等于2^n-1。
- -->
- <script type="text/javascript">
- var step=0;
- function MoveOnePlate(n, loca1, loca2)
- {
- document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");
- }
- function MovePlates(n, s, m, d)
- {
- //定义位置
- var loop=new Array(3);
- loop[0]=new Array(n);
- loop[1]=new Array(n);
- loop[2]=new Array(n);
- //定义位置描述字符串数组(n为偶数的情况)
- var loca=new Array(s, m, d);
- if (n%2!=0) //n为奇数的情况
- {
- loca[1]=d;
- loca[2]=m;
- }
- //初始化源位置上的盘子
- for(var i=0; i<n; i++)
- loop[0][i]=n-i;
- //记录各个位置上盘子的数量
- var loopLen=new Array(n, 0, 0);
- var count=Math.pow(2, n)-1; //移动次数,即循环退出条件
- var firstPlate=0; //1号盘子的位置
- do
- {
- //将1号盘子顺时针移动到后1个位置
- MoveOnePlate(1, loca[firstPlate], loca[(firstPlate+1)%3]); //显示移动过程
- loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]]=1; //移动
- loopLen[firstPlate]--; //修改1号盘子旧位置上盘子的数量
- firstPlate=(firstPlate+1)%3; //修改1号盘子的位置
- loopLen[firstPlate]++; //修改1号盘子新位置上盘子的数量
- count--; //记录移动次数
- //移动另外的两个位置上的盘子
- if(count!=0) //避免最后一次移动后仍然移动而导致错误
- {
- //确定另外两个位置如何移动
- if (loopLen[(firstPlate+1)%3]==0 || loopLen[(firstPlate+2)%3]!=0 &&
- loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1] < loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1] )
- { //1号盘子的后第1个位置为空,或者无空位置且1号盘子后第2个位置编号较小,此时将1号盘子后第2个位置的盘子移动到1号盘子后第1个位置上
- MoveOnePlate(loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1], loca[(firstPlate+2)%3], loca[(firstPlate+1)%3]); //显示移动过程
- loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]]=loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]-1]; //移动
- loopLen[(firstPlate+2)%3]--; //修改该盘子旧位置上盘子的数量
- loopLen[(firstPlate+1)%3]++; //修改该盘子新位置上盘子的数量
- }
- else
- { //1号盘子的后第2个位置为空,或者无空位置且1号盘子后第1个位置编号较小,此时将1号盘子后第1个位置的盘子移动到1号盘子后第2个位置上
- MoveOnePlate(loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1], loca[(firstPlate+1)%3], loca[(firstPlate+2)%3]); //显示移动过程
- loop[(firstPlate+2)%3][loopLen[(firstPlate+2)%3]]=loop[(firstPlate+1)%3][loopLen[(firstPlate+1)%3]-1]; //移动
- loopLen[(firstPlate+1)%3]--; //修改该盘子旧位置上盘子的数量
- loopLen[(firstPlate+2)%3]++; //修改该盘子新位置上盘子的数量
- }
- count--; //记录移动次数
- }
- } while(count!=0)
- }
- </script>
- </head>
- <body>
- <p>Hanoi Tower (Non-recursive algorithm) - 汉诺塔(非递归算法)<br />Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br />
- Copyright 2011, All right reserved. - 版权所有(C) 2011<br />2011.03.31</p>
- <script type="text/javascript">
- n=parseInt(prompt("请输入盘子的数量:", 3), 10);
- if (isNaN(n) || n<1 || n>16)
- {
- alert("请输入介于1到16的自然数!");
- location.reload();
- }
- else
- {
- document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n)-1) + "步:<br /><br />");
- MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");
- }
- </script>
- </body>
- </html>
非递归算法略微复杂些,代码里面有详细的注释和说明。
这里还有一个使用JavaScript数组对象的push()和pop()方法的非递归版本,可以简化程序,道理是一样的。
非递归算法版本2:
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
- <title>Hanoi Tower (Non-recursive algorithm, Version 2) - 汉诺塔(非递归算法,版本2)</title>
- <style type="text/css">
- i
- {
- color: #ff0080;
- }
- p
- {
- text-align: center;
- }
- </style>
- <!--
- 汉诺塔非递归算法:
- (1)、若问题规模n为偶数,按顺时针方向依次摆放s, m, d为环,若n为奇数,则为s, d, m;
- (2)、将盘子由小到大逐个编号并放置在s上,即最小的盘子编号为1,放在最上面;
- (3)、按顺时针方向把编号为1的盘子从当前的位置移动到下一位置;
- (4)、把另外两个位置(即除去1号盘子所在的位置)中非空的那个上的一个圆盘移到空的位置上,如果另外两个位置都非空,则移动编号较小的那一个;
- (5)、重复进行(3)和(4),直到移动的次数等于2^n-1。
- -->
- <script type="text/javascript">
- var step = 0;
- function MoveOnePlate(n, loca1, loca2) {
- document.write("第" + ++step + "步:移动第<i>" + n + "</i>个盘子,从" + loca1 + "到" + loca2 + "<br />");
- }
- function MovePlates(n, s, m, d) {
- //定义位置
- var loop = new Array([], [], []);
- //定义位置描述字符串数组(n为偶数的情况)
- var loca = new Array(s, m, d);
- if (n % 2 != 0) //n为奇数的情况
- {
- loca[1] = d;
- loca[2] = m;
- }
- //初始化源位置上的盘子
- for (var i = 0; i < n; i++)
- loop[0].push(n - i);
- var count = Math.pow(2, n) - 1; //移动次数,即循环退出条件
- var firstPlate = 0; //1号盘子的位置
- do {
- //将1号盘子顺时针移动到后1个位置
- MoveOnePlate(1, loca[firstPlate], loca[(firstPlate + 1) % 3]); //显示移动过程
- loop[(firstPlate + 1) % 3].push(loop[firstPlate].pop()); //从旧位置移动到新位置
- firstPlate = (firstPlate + 1) % 3; //修改1号盘子的位置
- count--; //记录移动次数
- //移动另外的两个位置上的盘子
- if (count != 0) //避免最后一次移动后仍然移动而导致错误
- {
- //确定另外两个位置如何移动
- 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]) {
- //1号盘子的后第1个位置为空,或者无空位置且1号盘子后第2个位置编号较小,此时将1号盘子后第2个位置的盘子移动到1号盘子后第1个位置上
- MoveOnePlate(loop[(firstPlate + 2) % 3][loop[(firstPlate + 2) % 3].length - 1], loca[(firstPlate + 2) % 3], loca[(firstPlate + 1) % 3]); //显示移动过程
- loop[(firstPlate + 1) % 3].push(loop[(firstPlate + 2) % 3].pop()); //从旧位置移动到新位置
- }
- else {
- //1号盘子的后第2个位置为空,或者无空位置且1号盘子后第1个位置编号较小,此时将1号盘子后第1个位置的盘子移动到1号盘子后第2个位置上
- MoveOnePlate(loop[(firstPlate + 1) % 3][loop[(firstPlate + 1) % 3].length - 1], loca[(firstPlate + 1) % 3], loca[(firstPlate + 2) % 3]); //显示移动过程
- loop[(firstPlate + 2) % 3].push(loop[(firstPlate + 1) % 3].pop()); //从旧位置移动到新位置
- }
- count--; //记录移动次数
- }
- } while (count != 0)
- }
- </script>
- </head>
- <body>
- <p>
- Hanoi Tower (Non-recursive algorithm, Version 2) - 汉诺塔(非递归算法,版本2)<br />
- Mengliao Software Studio(Baiyu) - 梦辽软件工作室(白宇)<br />
- Copyright 2011, All right reserved. - 版权所有(C) 2011<br />
- 2011.04.04</p>
- <script type="text/javascript">
- n = parseInt(prompt("请输入盘子的数量:", 3), 10);
- if (isNaN(n) || n < 1 || n > 16) {
- alert("请输入介于1到16的自然数!");
- location.reload();
- }
- else {
- document.write("共" + n + "个盘子,需移动" + (Math.pow(2, n) - 1) + "步:<br /><br />");
- MovePlates(n, "<i>源点</i>", "<i>临时</i>", "<i>目标</i>");
- }
- </script>
- </body>
- </html>
这里是三个算法网页源文件的连接:
http://img1.51cto.com/attachment/201104/876134_1301907387.rar