1. 程式人生 > >每天一道演算法題——漢諾塔

每天一道演算法題——漢諾塔

漢諾塔如圖所示,把圓盤從下面開始按大小順序重新擺放在另一根柱子上,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
它的解法可以採用分解法,把一個大的問題,逐步分解成一個個小問題。比如我們想把A中的盤子挪到B上,可以把問題分解成,將A的前n-1個盤子先挪到C,然後把A中最後一個挪到B,再把C的n-1個盤子挪到B;然後n-1個盤子的問題可以分解成,先將C中前n-2個盤子放到A,把C的最後一個盤子放到B,再把A中n-2個盤子放到B上。。。。。。這樣逐步遞迴,從而實現整個演算法。


這裡面還要說一下遞迴問題:編寫 遞迴函式時,必須告訴它何時停止遞迴。正因為如此,每個遞迴函式都有兩個部分:基線條件(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<String> stack = new Stack<String>();
	
	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<n; i++){
		for(int i=1; i<=n; i++) {	
			if(i != from && i != to){
				other = i;
				break;
			}
		}
		
		buildHanoi(from, other, top, bottom-1);
		stack.push(s);
		buildHanoi(other, to, top, bottom-1);
	}
	
	public void printStack(){
		if(stack.size() == 1){
			System.out.println(stack.pop()); 
			return;
		}
		
		String tmp = stack.pop();
		printStack();
		System.out.println(tmp);
	}

	public static void main(String[] args) throws Exception {
		// TODO Auto-generated method stub
		Scanner in = new Scanner(System.in);
		int n = in.nextInt();
		int from = in.nextInt();
		int to = in.nextInt();
		
		try{
			HanoiTower ht = new HanoiTower(n, from, to);
			ht.printStack();
		}catch(Exception e){
			e.printStackTrace();
		}
		
	}

}