前言
前言:简析「递归与分治」。
文章目录
一. 递归
1、什么是递归
- 递归的本质还是一种循环,但是这种循环不是通过
for
或者while
来实现,而是通过函数体进行循环。
2、特点
- 函数自身调用自身。
- 通过函数体来进行的循环(每一个函数体是一个子问题)。
- 以自相似的方法重复进行的过程。
3、关键点
- 定义好子问题。
- 定好边界。
4、模板
void recursion(int level, int param)
{
// terminator:终止条件
if (level > MAX_LEVEL) { //(1)
// process result:处理过程结果
return;
}
// process logic in current level:当前层的处理逻辑
process(level, param); //(2)
// drill down:向下一层
recur(level + 1, new_param); //(3)
// restore the current level status:恢复当前层状态 //(4)
}
- (1)终止条件;
- (2)处理本层信息;
- (3)递归处理子问题;
- (4)递归结束前要将所有修改过的全局变量还原;
- 注意:递归函数中的局部变量和参数是不需要恢复的,因为属于每一个函数体,结束时局部变量和参数自动被释放掉。而全局变量共享于所有的函数体(子问题),当一个递归结束时需要还原,一个子问题结束时要回到上一个子问题时,当前子问题对全局变量的修改需要还原成递归前的状态,在进入每一个函数体时,函数体之前的修改不能对后面产生影响。
二、树
1、结构
- 树是链表结构的扩展,每个节点都有左右指针,用于指向下一个节点。
2、遍历
1)树为什么适合用递归遍历
- 链表只有一个分叉,可直接采用 for 循环一层遍历;树分两个叉,采用递归进行遍历。
- 树结构天然具有递归的性质即子树的性质和整个树的性质一致;使用递归可以轻松地将整个树问题转换成子树问题。当层层递归到最小子树时,这个最小子树的解也称为递归出口往往很容易得到,然后再一步步向上回溯就能得到原问题的解。
2)树递归的思路
- 写出子问题的推导(归纳成一个子问题);
- 写出终止条件。
3)分类
a、前序遍历(自顶向下)
- 在每个递归层级上首先访问节点进行计算,并在递归调用函数时将这些值通过参数传递给子树。
b、后序遍历(自底向上)
- 首先对所有子节点递归调用函数,然后根据返回值和根节点本身的值得到答案,后序遍历依赖左右子树的返回值。
c、中序遍历
- 适用于二叉搜索树
4)总结
- 如果能使用参数和节点本身的值来决定应该传递给子节点的参数,就用前序遍历。
- 对于树中的任意一个节点,如果知道它子节点的答案,就能计算出当前节点的答案,就用后序遍历。
- 如果遇到二叉搜索树,就用中序遍历。