• 【算法】递归


    递归是一个比较难理解的问题,但是递归是一种非常广泛的算法,如数据结构中的树和图都会应用队规的算法。一般大家会拿汉诺塔进行举例,但是还有更容易理解的例子。


    周末你去看电影,然后你忘记了现在坐的是第几排了,电影院太黑了,看不清,没法数,怎么办?

    这个时候就可以问递归解决了,于是你问前面的人是第几排,你想只要在他的数字上加一,就知道自己是第几排了。但是,前面的人也看不清是第几排呀,于是他也问他前面的人。就这样一排一排往前问,直到问道第一排的人,说我在第一排,这样一排一排再把数字传回来。

    这是一个很标准的递归求解问题的分解过程。所有递归问题都可以用递归公式来表示。

    总结一下,就是一个递归要满足三个条件:

    1.一个问题的解可以分解成几个子问题的解

    2.这个问题与分解之后的子问题,除了数据规模不同,求解思路完全一样

    3.存在递归终止条件

    即写出一个递归函数,例子如下:

    f(n) = f( n-1) +1 其中,f(1) = 1

    改成代码为:

    #python 3
    def f(x:int) ->int:
        if ( n==1):
            return 1    
        return f( n-1) +1

    再举个例子,假如这里有 n 个台阶,每次可以跨一个或者俩个台阶,请问走这 n 个台阶有多少种走法?

    我们仔细想一下,实际上,我们可以根据第一步的走法把所有走法分为两类,第一类是第一步走了1个台阶,另一类是第一步走了2个台阶。用公式表达式为:

    f(n)=f(n-1)+f(n-2)

    我们在看一下终止条件,当只有一个台阶的时候,我们不需要再继续递归,所以f(1) =1,当 n=2 的时候,f(2)=f(1)+f(0),这里还需要一个f(0),但是这个情况看起来并不合理(因为不管怎么样我们都要跨一步或者俩步),所以我们把f(2)= 2也作为一种终止条件,表示走俩个台阶,有俩种走法,一步走完或者分俩步来走。

    f(n) = f(n-1) + f(n-2)  其中 f(1) = 1;f(2) = 2


    避免重复计算

    以刚才的例子为例,如果想要计算f(5),首先要计算f(4)和f(3),而计算f(4)还需要计算f(3),因此f(3)就被计算了多次,这就是重复计算四年的问题。

    为了避免重复计算,我们可以通过一个数据结构来保存已经求解过的f(k)。当递归调用到f(k)时,先看一下是否已经求解过了,如果是,直接从散列表种取值返回,如果不是,则添加进散列表。

    #python3
    di =dict() def f(n:int) -> int: global di if (n == 1) : return 1 if (n == 2) : return 2 if n in di.values(): return di.get(n) temp = f(n -1) +f(n-2) di[n]=temp return temp

    将递归改为非递归代码:

    笼统地讲,所有的递归本身都是借助栈来实现的,如果我们自己在内存堆上实现栈,手动模拟出栈入栈过程,所有的递归代码都可以改成不是递归代码的样子。

    如前面我们讲到的俩个例子,以第一个例子为例:

    #problem 1
    def f(n:int) -> int:
        ret  = 1
        for i in range(n):
            ret =ret +1
        return ret

    递归的缺点:

    在编写递归的过程中,往往会遇到很多问题,比如堆栈溢出。因为函数调用使用栈来保存临时变量,每调用一个函数,都会将临时变量封装为帧栈压入内存栈,等函数执行完成返回时,才出栈。如果递归求解的数据规模很大,调用层次很深,一直压入栈,就会有堆栈溢出的风险。

  • 相关阅读:
    【原创】大叔问题定位分享(21)spark执行insert overwrite非常慢,比hive还要慢
    【原创】大叔经验分享(14)spark on yarn提交任务到集群后spark-submit进程一直等待
    【原创】大叔问题定位分享(20)hdfs文件create写入正常,append写入报错
    【原创】大叔问题定位分享(19)spark task在executors上分布不均
    【原创】大数据基础之Spark(4)RDD原理及代码解析
    【原创】大叔问题定位分享(18)beeline连接spark thrift有时会卡住
    【原创】大叔问题定位分享(17)spark查orc格式数据偶尔报错NullPointerException
    【原创】大叔经验分享(13)spark运行报错WARN Utils: Service 'sparkDriver' could not bind on port 0. Attempting port 1.
    linux定时任务
    source导入错码解决办法
  • 原文地址:https://www.cnblogs.com/guangluwutu/p/11795527.html
Copyright © 2020-2023  润新知