问题分析
有三根圆柱,最终目的是把第一根圆柱X上的所有的圆盘按照顺序堆叠在第三根圆柱Z上,要求每一次只能移动一块圆盘,且大圆盘不能放在小圆盘的上面。
运用整体与局部的思想,这个问题可以简化为:假设有n个圆盘,我们把最上面的n-1个圆盘先移到第二根圆柱Y上①,再将第n个圆盘移动到圆柱Z上,再整体将n-1个圆盘移到圆柱Z上②。
对于①操作,如果我们忽略此时最下面的第n个圆盘,把X视作起始圆柱,把Y视作目标圆柱,那么就又回归到了上面的操作,直到只剩下一枚圆盘在当前的起始圆柱上面,这时我们只需要进行一次移动操作就可以完成问题的求解。
而对于②操作,我们把Y视作起始圆柱,把Z视作目标圆柱,那么剩下的X就是过渡圆柱,也划归到了之前的操作里面,直到仅剩下一枚圆盘。
所以这个问题最后只剩下了两种操作:整体移位和单体移位,把整体移位抽象为一个单独的函数,在函数内部递归调用自身,也就是汉诺塔问题的编程实现。
什么?你问我那个单体移位怎么搞?那个就是简单的输出语句,是结果!
代码实现
C语言代码
#include <stdio.h>
#include <stdlib.h>
//将n个盘子 从x 借助y 移动到z,表示一种整体的移动
//这是一种思路,而不是要具现出来的操作
void move(int n,char x,char y,char z){
if (n==1) {
//当这个整体的盘子数目为1的时候,也就得到了具体的实现步骤
printf("%c--->%c\n",x,z);
} else {
//move大于1个盘子的时候实际上都是表示整体的过程
move(n-1,x,z,y);
//我们要输出的是最终起到单体移位效果的那一步
//也就是把最下面的盘子移动到目标轴上
printf("%c--->%c\n",x,z);
move(n-1,y,x,z);
}
}
int main() {
move(3,'X','Y','Z');
return 0;
}
最后的输出结果如下:
X—>Z
X—>Y
Z—>Y
X—>Z
Y—>X
Y—>Z
X—>Z
JavaScript代码
在JavaScript实现的代码中,我定义了三个数组用于存储每个圆柱上的元素,每次单体移位的时候都使用了一次数组的栈方法模拟圆盘的移动,并将结果一并输出。
function move (n, start, transit, end, arr_x = X, arr_y = Y, arr_z = Z) {
let x;
if (n === 1) {
x = arr_x.pop();
arr_z.push(x);
console.log(start + " --" + x + "--> " + end, " X ", X, " Y ", Y, " Z ", Z);
} else {
move(n - 1, start, end, transit, arr_x, arr_z, arr_y);
x = arr_x.pop();
arr_z.push(x);
console.log(start + " --" + x + "--> " + end, " X ", X, " Y ", Y, " Z ", Z);
move(n - 1, transit, start, end, arr_y, arr_x, arr_z);
}
}
const hanoi_size = 3;
const X = [];
const Y = [];
const Z = [];
for (let i = hanoi_size; i > 0; i--) {
X.push(i);
}
move(hanoi_size, 'X', 'Y', 'Z', X, Y, Z);
最后的输出结果如下:
X --1–> Z X [ 3, 2 ] Y [] Z [ 1 ]
X --2–> Y X [ 3 ] Y [ 2 ] Z [ 1 ]
Z --1–> Y X [ 3 ] Y [ 2, 1 ] Z []
X --3–> Z X [] Y [ 2, 1 ] Z [ 3 ]
Y --1–> X X [ 1 ] Y [ 2 ] Z [ 3 ]
Y --2–> Z X [ 1 ] Y [] Z [ 3, 2 ]
X --1–> Z X [] Y [] Z [ 3, 2, 1 ]