• 递归


    递归是一种广泛的算法。
    其中用到了递归的数据结构和算法:DFS深度优先搜索、前中后序二叉树遍历等。
    递归公式:f(n)=f(n-1)+1 其中f(1)=1
     
    1.递归需要满足的三个条件
    • 一个条件的解可以分解为几个子问题的解
    • 这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样
    • 存在递归终止条件
    写递归代码的关键
    找到如何将大问题分解为小问题的规律,并基于此写出递推公式,然后再推敲出终止条件,最后将其翻译为代码
     
    注:对于递推,不要试图用人脑去分解递归的每个步骤,关键是抽象出递归公式,和找到终止条件
    2.递归代码要警惕堆栈溢出
    我们在栈那一节有讲过,函数调用会使用栈来保存临时变量。
    每调用一个函数,都会将临时变量封装为帧栈压入内存栈,等函数执行完成时,才出栈。
    而系统栈或者虚拟机栈空间一般都不大。
    如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。

    那么,要怎么避免出现堆栈溢出呢?
    我们可以通过在代码中限制递归调用的最大深度的方式来解决。
    就是递归调用超过一定深度之后,我们就不继续往下递归了,直接返回报错。
    如下:

    因为最大允许的递归深度跟当前线程剩余的栈空间大小有关,实现无法计算,所以问题并不能完全解决。
    而实时计算,代码过于复杂,影响可读性。
    所以如果最大深度比较小,就可以用这种方法,否则这种方法并不实用。
     
    3.递归代码要警惕重复计算
    如下例子:

    从图中,我们看出,
    想要计算f(5),需要先计算f(4)和f(3),
    而计算f(4)还需要计算f(3),
    因此f(3)被计算了很多次,这就是重复计算问题。
     
    为了避免重复问题,我们可以通过一个数据结构(比如散列表)来保存已经求解过的f(k)。
    当递归调用到f(k)时,先看下是否已经求解过了。
    如果是,则直接从散列表中取值返回,不需要重复计算,这样就可以避免重复计算了。
     
    如下;
    递归公式:f(n) = f(n-1) + f(n-2);终止条件:f(1)=1,f(2)=2;
    初始代码:

    避免重复之后的代码:

    递归除了堆栈溢出、重复计算两个问题,还有一些其他问题
    在时间效率上,递归代码里多了很多的函数调用,
    当这些函数调用的数量较大时,就会积聚成一个可观的时间成本。
    在空间复杂度上,因为递归调用一次就会在内存中保存一次现场数据,
    所以在分析代码空间复杂度时,需要额外考虑这部分的开销。
     
    4.把递归代码改写为非递归代码
    递归有利有弊;利是递归代码表达能力很强,写起来简洁;
    而弊就是空间复杂度高,有堆栈溢出风险,
    存在重复计算,过多的函数调用会耗时过多等问题。
     
    所以,在开发过程中,我们要根据实际情况来选择是否需要用递归来实现代码。
    如下:递归的代码改为非递归

    是否所以的递归代码可以改为这种迭代循环的非递归写法呢?
    笼统的讲,可以。
    因为递归本身就是借助栈来实现的,只不过我们使用的栈是系统或者虚拟机本身的。
    如果我们自己在内存堆上实现栈,手动模拟入栈,出栈过程,
    这样任何代码都可以改写成看上去不是递归代码的样子。
    但是,这种方式,实际上只是将递归改成了手动递归,本质并没变
    也没有解决前面讲到的问题,只是增加了复杂度。
    5.如何找到最终推荐人
    如下:

    对于上面的代码,存在两个问题:
    第一,如果递归很深,可能会有堆栈溢出的问题
    第二,如果数据库存在脏数据,需要处理由此产生的无线递归问题。
     
    对于第一个问题,我们可以用限制递归深度的方法解决。
    对于第二个问题,也可以用限制递归深度的来解决。但是,其实还可以用自动检测“A-B-C-A”这种环的纯在。
    如何检测环呢?
     
    课后思考:
    我们平时调试喜欢用IDE的单步跟踪功能,但是像规模比较大、递归层次很深的递归代码,几乎无法使用这种调试方式。对于递归代码,你有什么好的调试方式?
  • 相关阅读:
    Game的基本元素.[小糊涂的灵感]
    J2ME图书介绍 [小糊涂的灵感]
    j2me 这个论坛好一点.[小糊涂的灵感]
    Frame rate test for tilebased games 测试结果.[小糊涂的灵感]
    源码方式在ubuntu系统上安装ruby1.9.2
    模块全解======>>ruby的类是单继承生物、所以出现了module、实现了多继承
    在ubuntu下安装rails3.0
    在ubuntu下编写运行shell脚本
    在linux下开远程桌面访问windows的解决方法
    在命令行中打开sqlite的数据库
  • 原文地址:https://www.cnblogs.com/Vincent-yuan/p/12853377.html
Copyright © 2020-2023  润新知