1. 程式人生 > >《演算法筆記》11. 暴力遞迴思維、動態規劃思維

《演算法筆記》11. 暴力遞迴思維、動態規劃思維

[TOC] # 1 暴力遞迴、動態規劃 > 轉載註明出處,原始碼地址: https://github.com/Dairongpeng/algorithm-note ,歡迎star ## 1.1 暴力遞迴思維 **==暴力遞迴實質就是嘗試==** > 概念解釋: > 回溯-表示大問題被拆解為小問題,小問題返回給大問題資訊,就是回溯 > 分治:大問題被拆解成小的子問題,就是分治 1、把問題轉化為規模縮小了的同類問題的子問題 2、有明確的不需要繼續進行遞迴的條件(base case) 3、有當得到了子問題的結果之後的決策過程 4、不記錄每個子問題的解(如果記錄每個子問題的解,就是我們熟悉的動態規劃) ### 1.1.1 暴力遞迴下的嘗試 #### 1.1.1.1 例一:漢諾塔問題 列印n層漢諾塔從最左邊移動到最右邊的全部過程 > 漢諾塔圓盤移動,如果杆子上沒有圓盤,可以移動到該杆,如果有圓盤則必須移動比該圓盤小的圓盤到該圓盤上 > 思路1:1. 先想辦法把1到N-1層圓盤移動到中間杆,2. 再把N層的圓盤移動到最右側的杆上 3. 把1到N-1個圓盤從中間杆移動到最右側。結束 > 思路2:忘掉左中右,理解為從from移動到to,from和to都有可能是左中右。所以定義from,to,other三個杆子。1. 把1到N-1移動到other上。2. 把第N層移動到to上。3. 把1到N層從other移動到to上。結束 > 思路3:遞迴改非遞迴實現 > N層漢諾塔,從左移動到右最優步數是2^N - 1 步。遞迴公式 T(N) = T(N-1) + 1 + T(N-1)。化簡為等比數列,高中數學內容 嘗試是有優劣之分的,譬如思路1和思路二。在動態規劃章節,可以用動態規劃優化我們的嘗試到最優版本 ```Java package class11; import java.util.Stack; public class Code01_Hanoi { // 按照思路1的方法 public static void hanoi1(int n) { leftToRight(n); } // 請把1~N層圓盤 從左 -> 右 public static void leftToRight(int n) { if (n == 1) { System.out.println("Move 1 from left to right"); return; } leftToMid(n - 1); System.out.println("Move " + n + " from left to right"); midToRight(n - 1); } // 請把1~N層圓盤 從左 -> 中 public static void leftToMid(int n) { if (n == 1) { System.out.println("Move 1 from left to mid"); return; } leftToRight(n - 1); System.out.println("Move " + n + " from left to mid"); rightToMid(n - 1); } public static void rightToMid(int n) { if (n == 1) { System.out.println("Move 1 from right to mid"); return; } rightToLeft(n - 1); System.out.println("Move " + n + " from right to mid"); leftToMid(n - 1); } public static void midToRight(int n) { if (n == 1) { System.out.println("Move 1 from mid to right"); return; } midToLeft(n - 1); System.out.println("Move " + n + " from mid to right"); leftToRight(n - 1); } public static void midToLeft(int n) { if (n == 1) { System.out.println("Move 1 from mid to left"); return; } midToRight(n - 1); System.out.println("Move " + n + " from mid to left"); rightToLeft(n - 1); } public static void rightToLeft(int n) { if (n == 1) { System.out.println("Move 1 from right to left"); return; } rightToMid(n - 1); System.out.println("Move " + n + " from right to left"); midToLeft(n - 1); } // 思路二:暴力遞迴 from to other public static void hanoi2(int n) { if (n > 0) { func(n, "left", "right", "mid"); } } // 1~i 圓盤 目標是from -> to, other是另外一個 public static void func(int N, String from, String to, String other) { if (N == 1) { // base System.out.println("Move 1 from " + from + " to " + to); } else { func(N - 1, from, other, to); System.out.println("Move " + N + " from " + from + " to " + to); func(N - 1, other, to, from); } } public static class Record { public boolean finish1; public int base; public String from; public String to; public String other; public Record(boolean f1, int b, String f, String t, String o) { finish1 = false; base = b; from = f; to = t; other = o; } } // 思路三:非遞迴實現 public static void hanoi3(int N) { if (N < 1) { return; }