每天一道演算法題——漢諾塔
阿新 • • 發佈:2019-02-06
漢諾塔如圖所示,把圓盤從下面開始按大小順序重新擺放在另一根柱子上,在小圓盤上不能放大圓盤,在三根柱子之間一次只能移動一個圓盤。
它的解法可以採用分解法,把一個大的問題,逐步分解成一個個小問題。比如我們想把A中的盤子挪到B上,可以把問題分解成,將A的前n-1個盤子先挪到C,然後把A中最後一個挪到B,再把C的n-1個盤子挪到B;然後n-1個盤子的問題可以分解成,先將C中前n-2個盤子放到A,把C的最後一個盤子放到B,再把A中n-2個盤子放到B上。。。。。。這樣逐步遞迴,從而實現整個演算法。
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個鐵餅移動到臨時杆上,再把最後一個鐵餅移動到目標杆上。
它的解法可以採用分解法,把一個大的問題,逐步分解成一個個小問題。比如我們想把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)。遞迴條件是指函式呼叫自己,而基線條件則是指函式不再呼叫自己,從而避免形成無線迴圈。
(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(); } } }