题目描述
传说中有一座位于印度寺庙的塔,塔内有三个柱子,其中一个柱子上摞着n个大小不同的圆盘,从底部开始呈递减的形式。目标是将这些圆盘按照规定的规则从初始柱子(通常是最左边的柱子)移动到目标柱子(通常是最右边的柱子),并且在整个过程中遵守以下规则:
1、每次只能移动一个圆盘。
2、不能将较大的圆盘放在较小的圆盘
求移动到右边柱子的最少需要的移动次数。
问题分析
以一个三阶汉诺塔为例,我们先观察其移动过程
再看下四阶的汉诺塔移动过程
从上面的两个例子中,我们可以归纳总结,n阶的汉诺塔,在移动时,都需要经过上面红框里面的三步,
1、先将前面n-1个圆盘移到中间的柱子上;
2、再将最大的圆盘从左边移到右边的柱子上;
3、最后将中间柱子上的n-1个圆盘移到右边的柱子上;
而n-1阶又可以由n-2阶推导出,以此类推,最后可推到1阶汉诺塔,然后从1阶的最少移动次数为1出发,算出2阶的最少移动次数为 1+1+1,3阶的最少移动次数为 3+1+3,以此类推。其实不难发现,n阶汉诺塔的最少移动次数其实就等于(2^n) -1,写成函数表达式则是f(n)=f(n-1)+1+f(n-1),表达式中的三部分正好对应上面的三个步骤,所以经过上述分析,怎么用代码实现是不是呼之欲出。
代码实现
/**
*
* @param n 代表汉诺塔阶数
* @return 返回最少移动次数
*/
private int getHanoi(int n) {
Stack src = new Stack();
Stack mid = new Stack();
Stack dest = new Stack();
return compute(n,src,mid,dest);
}
/**
*
* @param n 代表汉诺塔阶数
* @param src 左边的柱子
* @param mid 中间的柱子
* @param dest 右边的柱子
* @return 返回最少移动次数
*/
private int compute(int n, Stack src, Stack mid, Stack dest) {
if(n == 1) {
return 1;
}else {
//先将前面n-1个圆盘移到中间的柱子上
int result = compute(n-1,src,dest,mid);
//再将最大的圆盘从左边移到右边的柱子上
result++;
//最后将中间柱子上的n-1个圆盘移到右边的柱子上
result = result + compute(n-1,mid,src,dest);
return result;
}
}
解法2:动态规划
由前面的分析,我们知道n阶汉诺塔的函数表达式为f(n)=f(n-1)+1+f(n-1),由此,我们可以直接复用建立状态转移方程f(n)=f(n-1)+1+f(n-1)
PS:使用动态规划算法时,状态转移方程有几个变量,我们就建立几维数组来存储中间结果
代码实现
/**
*
* @param n 代表汉诺塔阶数
* @return 返回最少移动次数
*/
public int getHanoi(int n) {
if(n == 1) {
return 1;
}
int[] data = new int[n];
data[0] = 1;
for(int i = 1; i < n; i++){
data[i] = data[i-1] + 1 + data[i-1];
}
return data[n-1];
}