《演算法筆記》11. 暴力遞迴思維、動態規劃思維
阿新 • • 發佈:2020-08-10
[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;
}