Files
Learn-Algorithms/8 Algorithms Analysis/递归.md
T
2021-11-05 10:34:02 +08:00

148 lines
3.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 递归
递归是一种设计和描述算法的有力工具。 递归算法执行过程分 `递推` 和 `回归` 两个阶段
在 `递推` 阶段,将大的问题分解成小的问题
在 `回归` 阶段,获得最简单问题的解后,逐级返回,依次得到稍微复杂情况的解,知道获得最终的结果
1) 确定递归公式
2) 确定边界条件
> 自顶向下的递归,自底向上是迭代
递归运行效率较低,因为有函数调用的开销,递归多次也可能造成栈溢出。
### 其它案例
* 阶乘计算
* 梵塔问题 (三根针1,2,3表示,1号从小到大n个盘子,先要都移到3号上,不能出现大盘压小盘,找出移动次数最少的方案)
* 快速排序
* 很多树算法都是递归思想实现
## 斐波那契数列
fib(n)=fib(n-1)+fib(n-2)
#### 递归实现
```
int fib(int N) {
if (N == 1 || N == 2) return 1;
return fib(N - 1) + fib(N - 2);
}
```
这个递归的算法效率非常差,存在大量重复计算。
#### 非递归实现
我们可以有一个【缓存】,每次遇到一个子问题先去「缓存」里查一查,如果发现之前已经解决过这个问题了,直接把答案拿出来用,不要再耗时去计算了。
```
public int fib(int n){
if (n == 0) return 0;
//缓存
int[] dp = new int[n + 1];
// base case
dp[0] = 0; dp[1] = 1;
// 状态转移
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + dp[i - 2];
}
return dp[n];
}
```
说一个细节优化点,当前状态只和之前的两个状态有关,其实并不需要那么长的一个 DP table 来存储所有的状态,只要想办法存储之前的两个状态就行了。所以,可以进一步优化,把空间复杂度降为 O(1):
```
int fib(int n) {
if (n < 1) return 0;
if (n == 2 || n == 1)
return 1;
int prev = 1, curr = 1;
for (int i = 3; i <= n; i++) {
int sum = prev + curr;
prev = curr;
curr = sum;
}
return curr;
}
```
## 合并有序链表
递归解法
```
public static LinkedNode mergeSeqLink2(LinkedNode l1, LinkedNode l2){
if(l1 == null){
return l2;
}
if(l2 == null){
return l1;
}
if(l1.value < l2.value){
l1.next = mergeSeqLink2(l1.next,l2);
return l1;
}else{
l2.next = mergeSeqLink2(l2.next,l1);
return l2;
}
}
```
非递归解法
```
public static LinkedNode mergeSeqLink(LinkedNode l1, LinkedNode l2){
if (l1 == null) return l2;
if (l2 == null) return l1;
LinkedNode result = new LinkedNode(0);
LinkedNode tmp = result;
while (l1 != null && l2 != null) {
if (l1.value < l2.value) {
tmp.next = l1;
tmp = tmp.next; //tmp 指针前进
l1 = l1.next ; //l1 前进
} else {
tmp.next = l2;
l2 = l2.next;
tmp = tmp.next;
}
}
if (l1 != null) {
tmp.next = l1;
}
if (l2 != null) {
tmp.next = l2;
}
return result.next;
}
```