# 算法基础--递归和动态规划

### 暴力递归

1. 把问题转化为规模缩小了的同类问题的子问题

2. 有明确的不需要继续递归的条件

base case

### 求n!的结果

#### 非递归版本

``````func getFactorial1(n : Int) -> Int {
var res = 1
for i in 1..<n+1 {
res = res * i
}

return res
}
``````

#### 递归版本

``````func getFactorial2(n : Int) -> Int {
if n == 1 {
return 1
}
return n * getFactorial2(n: n-1)
}
``````

### 汉诺塔问题

image

image
``````/// 移动1-N层汉诺塔
///
/// - Parameters:
///   - n: 需要移动到的层数
///   - form: 从哪根开始
///   - to: 从哪根结束
///   - help: 空那根
func hanoiGame(n : Int ,form :String ,to :String ,help :String) {
if n == 1 {//只移动第一层，直接移动即可
print("Move 1 from " + form + " to " + to)
}else {
hanoiGame(n: n-1, form: form, to: help, help: to)  //将第 1到n-1 层移动到 中间
print("Move \(n) " + "from " + form + " to " + to) //将第 n 层移动到 最右
hanoiGame(n: n-1, form: help, to: to, help: form) //将第 1到n-1 层移动到 最右
}
}

hanoiGame(n: 3, form: "左", to: "右", help: "中")
//打印
Move 1 from 左 to 右
Move 2 from 左 to 中
Move 1 from 右 to 中
Move 3 from 左 to 右
Move 1 from 中 to 左
Move 2 from 中 to 右
Move 1 from 左 to 右
``````

### 打印字符串能组成的所有字串

``````func printStr(str :String) {
printAllSub(str: wordToArr(word: str), i: 0, res: "")
}

func printAllSub(str :[String] ,i :Int ,res :String) {
if i == str.count {
print(res)
}else {
printAllSub(str: str, i: i+1, res: res+str[i]) //打印当前位置
printAllSub(str: str, i: i+1, res: res) //不打印当前位置
}

}

func wordToArr(word:String) -> Array<String> {
var res : [String]
res = Array.init()
if word.count == 0 {
return res
}
let string = (word as NSString)
for i in 0..<string.length {
res.append(string.substring(with: NSMakeRange(i, 1)))
}

return res
}

``````

### 母牛数目问题

image

F(N) = F(N-1) + F(N-3)

``````func func(n : Int) -> Int {
if n == 1 {
return 1
}
if n - 3 <= 0 {
return func1(n: n-1) + 1
}else {
return func1(n: n-1) + func1(n: n-3)
}
}
``````

### 二维数组--从左上角到右下角最大值

``````/// 二维数组--从左上角到右下角最大值
///
/// - Parameters:
///   - matrix: 二维矩阵
///   - x: x轴坐标
///   - y: y轴坐标
/// - Returns: 当前点到右下角最小距离
func walk(matrix : [[Int]] ,x :Int ,y :Int) -> Int {
if (x == matrix.count-1) && (y == matrix[0].count-1) { //已经到最后
return matrix[x][y] //返回当前节点
}

if x == matrix.count-1 {  //已经到x轴末尾
return matrix[x][y] + walk(matrix: matrix, x: x, y: y+1) //当前节点+y轴下一位
}

if y == matrix[0].count-1 { //已经到y轴末尾
return matrix[x][y] + walk(matrix: matrix, x: x+1, y: y) //当前节点+x轴下一位
}

//当前节点+min(x轴下一位,y轴下一位)
return matrix[x][y] + min(walk(matrix: matrix, x: x+1, y: y), walk(matrix: matrix, x: x, y: y+1))
}

``````

image

image

### 动态规划

#### 无后效性递归如何改成动态规划的通用方法

`二维数组--从左上角到右下角最大值`题目为例：

1. 分析可变参数，建立状态表

以每个状态的return结果建立一个二维数组。

2. 找到自己需要的最终状态位置(0,0)

image
3. 回到base case 中，对不被依赖的位置进行设置

image
4. 对普遍位置进行设置

image
5. 最终得到目标位置

### 数组中元素是否能组成指定的和

``````/// 数组中元素是否能组成指定的和
///
/// - Parameters:
///   - arr: 数组
///   - i: 当前位置
///   - sum: 已经求的和
///   - aim: 目标和
/// - Returns: 结果
func isSum(arr :[Int] ,i :Int ,sum :Int ,aim :Int) -> Bool {
if i == arr.count { //数组末尾已经尝试结束
return aim==sum //直接比对
}

let useC = isSum(arr: arr, i: i+1, sum: sum+arr[i], aim: aim) //尝试添加当前位置

let unuseC = isSum(arr: arr, i: i+1, sum: sum, aim: aim) //不添加当前位置

return useC || unuseC
}
``````

#### 如何转变成动态规划

1. 简化表达式，并建立动态规划表

只有两个可变参数，可以简化成`F(i,sum)`

DP表的设计行为sum（最后一位为所有元素之和)，列为i。
在代码上，将作为一个二维数组存在

image
2. 确定目标位置

image
3. base case中找到不被依赖的位置
只有在F(N,Aim)时，`aim==sum`才会返回`true`

image

4. 对普遍位置进行设置
某一个位置`F(i,sum1)`的状态依赖于`F(i+1,sum1)``F(i+1,sum1)+arr[i]`
`F(i+1,sum1)+arr[i]`又作为新的sum值`Sum2`存在于DP表内。
两个位置有一个为Aim，则将返回true

image

5. 推回到最初位置