[知识点] 2.2 递归与分治

总目录 > 2 算法基础 > 2.2 递归与分治

前言

递归是一种非常常用的算法思想,太多太多地方使用了。

(总目录:https://www.cnblogs.com/jinkun113/p/12528423.html

子目录列表

1、递归

2、分治

2.2 递归与分治

1、递归

递归的基本思想是函数直接或间接地调用自身,用两张图来分辨递归与枚举的区别:

枚举是按部就班从起点到终点逐一处理或累加;递归时的求解过程就像剥洋葱皮一样一层层地深入,直到得到最终结果。所以,递归的代码往往为如下结构:

int func(当前状态) {
    if (终止条件) return 最终结果;
    return func(下一状态);
}

也就是说,打一开始,我们就要知道我们求解的目的是什么,终点在何方。相比枚举,递归其实是一种逆向思维,因为它的主要步骤是从最外层先找到最里层,然后再从最里层开始处理问题。

从结构上来看,递归的三大核心:

① 当前状态

② 终止条件

③ 下一状态

明确了这三点,递归基本就成型了。

在不建立在某种算法或者某种结构的前提下,其实单说递归是空洞的,它是作为各种算法的底层思想,所以如果要去理解它,去了解用到递归的算法要比了解递归的本质要来的轻松。所以,这里只给出几个密切相关的链接和一些代码,暂且不提算法本身的作用,只是用以体现递归的常用形式。

① 深度优先搜索 DFS(请参见:3.1 DFS / BFS

 1 void DFS(int x, int y, int p) {
 2     if (a[x][y] == 1) return;
 3     a[x][y] = 1;
 4     if (x == tx && y == ty) ans = min(p, ans);
 5     else for (int i = 1; i <= 4; i++) {
 6         int vx = x + cx[i], vy = y + cy[i];
 7         if (a[vx][vy] == 0) DFS(vx, vy, p + 1);
 8     }
 9     a[x][y] = 0;
10 }

DFS 的过程绝对是对递归思想最好的诠释,整个搜索框架完全是用递归实现的,这里给出的是 0-1 矩阵走迷宫代码只是其中一种。

② 树结构的构建与遍历(请参见:7.3  树与二叉树

③ 归并排序(请参见:2.4 排序十讲 中的归并排序部分)

④ 最大公约数(请参见:6.4.1 素数与最大公约数

1 int gcd(int x, int y) {
2     return y ? gcd(y, x % y) : x; 
3 }

⑤ 线段树的区间询问操作(请参见:<施工中>)

1 int quem(int o, int l, int r) {
2     if (t[o].f) push(o, r - l + 1);
3     if (ql <= l && r <= qr) return t[o].v;
4     int m = (l + r) >> 1;
5     return (ql <= m ? quem(ls, l, m) : 0) + (qr >= m + 1 ? quem(rs, m + 1, r) : 0);
6 }

因为线段树本身就是树结构,所以其实它也可以归类于②。

2、分治

其实分治是递归中的一种,同样用一张图来体现它的特点:

相较于非分治的递归,分治的特点在于:

① 对于每一层向内并不是只递归一次,一般是两次,但更多也可以;

② 对于任何元素,其在递归过程中只会被访问一次。

在递归部分给出的若干份代码中,线段树的区间询问函数归并排序都属于分治思想。

分治思想的核心在于:分解,解决,合并

因为同样是一种思想,所以这里也不作更多的解释,参见各种算法对于理解可能会来得更直接一点。

原文地址:https://www.cnblogs.com/jinkun113/p/12797469.html