一.递归模型
分而治之的思想也就是典型的递归思想,递归思想的核心就是
递归模型
的建立
,递归模型
就是处理这类问题的一个
相同的框架,这个框架不仅仅是处理总问题的框架,也是处理组成总问题的子问题的框架,这个框架具有公用性,要适用这种公用性,就可以推断得出,这类问题的结构就有递归性质(
从前有座上,山上有座庙,庙里有个老和尚,老和尚对小和尚说:从前有座上,山上有座庙,庙里有个老和尚,老和尚对小和尚说。。。。。。。
)
由此可以得出处理递归方法的套路:
找出一个
通用模型:
通用模型
{
- 递归的传递
- 递归的出口(隐含了递归的返回)
- 递归的处理
}
所以一个通用的递归模型就是由上面两部分组成的;
【递归的传递】
是保证可以把
母问题
划分为
规模
比原问题
小
的
子问题
;体现分而治之的
分;
【递归的出口】
保证递归是可穷尽的,在穷尽处实现分而治之的
治
(第一次治)(
把复杂问题转换为简单子问题再进行处理,)到达出口,在处理(治)完成以后,会自动返回;
【递归的处理】
也就是分而治之的
“治”
,在每个级别规模的问题中都用共同的一套递归处理,只不过实现顺序有先后之分,注意
递归出口的处理
和
递归处理
其实是不同的,递归出口处的处理一般是结束返回,以边界值判断,而递归处理是一套共用的处理方法;
【递归三要素的顺序关系】
我们通常判断递归出口条件以决定递归是否向下执行的先决条件,然后才是递归的传递和递归的处理(后两者顺序依情况而定),这里递归出口有两种形式:
1.递归出口的类型
(1)返回型出口(
一定要注意返回值不要丢失
)
例1:求二叉树的高度的递归算法:
- 递归出口的处理:return 0;
- 递归处理: return i > j ? i + 1 : j + 1;
例2:寻找二叉树任意结点的父节点
错误代码示范:
上述类型的错误非常容易出现:出现的原因是对
return返回
理解的不透彻造成的;
return返回的接收者:只能返回给调用自己那个函数,不会越级返回给父函数的上一层或上上层,总的一句话:
谁调用返给谁;
上述的代码中,在递归的出口处(注意,这个出口是由
两个出口
组成的:当subtree为空或者找到了父节点(subtree->leftnode == current || subtree->rightnode == current))进行返回,这个返回只会给调用它的父函数,
父函数如果想继续向上传递,必须自己再次返回
,否则,返回值丢失;
正确代码示范:
红色标注是父函数自己的返回;
(2)逻辑型出口
例:
if(i>0)就是逻辑型出口,不做任何返回,
这种上一层实现不依赖下一层结果的递归最好用循环代替(节省空间)
2.递归出口、递归处理、递归传递三者的顺序关系
递归的传递:
保证递归分治思想,即把大问题划分为同等结构的子问题;
递归出口:
确保递归传递的可穷尽性,所以肯定在
递归传递
之前;
递归处理:
递归的使用场景一般是上一层的结果的计算需要依赖下一层的结果,所以递归的处理一般在递归的传递之后(传递下去再返回才能进行这层的计算);
所以一般递归的模型中这三者的顺序如下:
递归{
1 递归的出口;
2 递归的传递;
3 递归的处理;
}
这只是一般通常的情况,也有不同,比如上面寻找二叉树父节点的递归就是
递归的处理
在
递归的传递
之前,为什么呢?查找操作肯定是查到立刻返回,就没必要遍历全部结点了(没必要向下传递了)且结果没有依赖性(结果的得出没有次序性),上层递归只是负责传递下层返回的参数,自己不加工。
二.常见的使用递归的场景
(1)二叉树:二叉树的结构就是递归的,所以对他的操作一般考虑递归
二叉树前中后序遍历;
二叉树的高度;
二叉树的镜像;
二叉树的个数;
二叉树的最大值;
二叉树的删除;
二叉树的寻父节点;
二叉树的建立:
二叉树的复制;
(2)阶乘:
三.递归形象的理解
(1)查单词:
遇到一个不理解的单词,维基百科上查解释,在这个解释中又有不理解的单词,又去查这个单词的解释。。。。。。终于有一个词条里每个词都能看懂了,这时候就返回,继续看上一个词的解释。
(2)递纸条:
上课传纸条,你传给你的右手边,右上边的人又传给他的右手边人。。。。纸条传到了,写上回话(递归出口)后,就往回传(递归返回)