每天一道算法题——汉诺塔

汉诺塔如图所示,把圆盘从下面开始按大小顺序重新摆放在另一根柱子上,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
它的解法可以采用分解法,把一个大的问题,逐步分解成一个个小问题。比如我们想把A中的盘子挪到B上,可以把问题分解成,将A的前n-1个盘子先挪到C,然后把A中最后一个挪到B,再把C的n-1个盘子挪到B;然后n-1个盘子的问题可以分解成,先将C中前n-2个盘子放到A,把C的最后一个盘子放到B,再把A中n-2个盘子放到B上。。。。。。这样逐步递归,从而实现整个算法。

每天一道算法题——汉诺塔_第1张图片

这里面还要说一下递归问题:编写 递归函数时,必须告诉它何时停止递归。正因为如此,每个递归函数都有两个部分:基线条件(base case)和递归条件(recursive case)。递归条件是指函数调用自己,而基线条件则是指函数不再调用自己,从而避免形成无线循环。

D&C(divide and conquer)分而治之—— 一种递归式问题解决方案。
(1)找出基线条件,这种条件必须尽可能简单。
(2)不断将问题分解(缩小规模),知道符合基线条件。


汉诺塔问题是NP完全问题,也就是说,它的算法时间复杂度是指数级的,分析如下,假设我们要挪动n个铁饼,把时间标记为T(n), 我们先挪动n-1个铁饼,所需时间就是T(n-1), 然后再挪动一个铁饼,时间为O(1), 然后再挪动n-1个铁饼,时间为T(n-1), 于是我们有:      T(n) = 2*T(n-1) + O(1). 
这个公式把T(n)解出来结果为:  T(n) = 2^n;
这就意味着,每增加一个铁饼,所需的挪动步骤几乎是原来的两倍


下面来看程序:
在HanoiTower类中声明三个变量,n表示有多少个盘子,from,to表示从from移动到to杆上。
首先判断一下边界条件(这是一个良好的编程习惯),之后调用buildHanoi方法实现递归。
在buildHanoi方法中,我们要先找到基线条件:就是到参数top和bottom相同的时候,也就是说指向塔尖的指针和指向塔底的指针重合,那就意味着只有一个盘子,说明递归到头可以跳出了。而递归条件就如上面所讲的
buildHanoi(from, other, top, bottom-1);将n-1个铁饼移动到临时杆上,再把最后一个铁饼移动到目标杆上。

buildHanoi(other, to, top, bottom-1);最后将临时杆上的铁饼移动到目标杆上。

package p_11_hanoi;

import java.util.Scanner;
import java.util.Stack;

public class HanoiTower {
	private int from = 0;
	private int to = 0;
	private int n = 0;
	Stack stack = new Stack();
	
	public HanoiTower(int n, int from, int to) throws Exception{
		if(n<=0 || from>n || from<0 || to>n || to<0){
			throw new Exception("this is a invalid parameter!");
		}
		
		this.n = n;
		this.from = from;
		this.to = to;
		
		buildHanoi(this.from, this.to, 1, n);
	}
	
	public void buildHanoi(int from, int to, int top, int bottom){
		String s = "Moving " + bottom + " from " + from + " to " + to;
		if(top == bottom){
			stack.push(s);
			return;  // add!!!
		}
		
		int other = from;
		//for(int i=0; i


你可能感兴趣的:(java)