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

漢諾塔如圖所示,把圓盤從下面開始按大小順序重新擺放在另一根柱子上,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
它的解法可以採用分解法,把一個大的問題,逐步分解成一個個小問題。比如我們想把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();
		}
		
	}

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章