zoukankan      html  css  js  c++  java
  • 递归总结

    有段话来理解递归,感觉不错:

    你打开面前这扇门,看到屋里面还有一扇门。你走过去,发现手中的钥匙还可以打开它,你推开门,发现里面还有一扇门,你继续打开它。若干次之后,你打开面前的门后,发现只有一间屋子,没有门了。然后,你开始原路返回,每走回一间屋子,你数一次,走到入口的时候,你可以回答出你到底用这你把钥匙打开了几扇门。

    本质上讲,在计算机中,函数调用是通过(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

    递归的精髓

    递归就是有去(递去)有回(归来),如下图所示。“有去”是指:递归问题必须可以分解为若干个规模较小,与原问题形式相同的子问题,这些子问题可以用相同的解题思路来解决,就像上面例子中的钥匙可以打开后面所有门上的锁一样;“有回”是指 : 这些问题的演化过程是一个从大到小,由近及远的过程,并且会有一个明确的终点(临界点),一旦到达了这个临界点,就不用再往更小、更远的地方走下去。最后,从这个临界点开始,原路返回到原点,原问题解决。 

    递归的三要素:

    1.明确递归终止条件;
    2.给出递归终止时的处理办法;
    3.提取重复的逻辑,缩小问题规模.

     补充:

    我们说递归效率不高,因为每递归一次,就多了一层栈,递归次数太多还会导致栈溢出,这也是为什么python会默认限制递归次数的原因。但有一种方式是可以实现递归过程中不产生多层栈的,即尾递归。

    尾递归

    如果一个函数中所有递归形式的调用都出现在函数的末尾,我们称这个递归函数是尾递归的。当递归调用是整个函数体中最后执行的语句且它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。尾递归函数的特点是在回归过程中不用做任何操作,这个特性很重要,因为大多数现代的编译器会利用这种特点自动生成优化的代码。

    当编译器检测到一个函数调用是尾递归的时候,它就覆盖当前的活动记录而不是在栈中去创建一个新的。编译器可以做到这点,因为递归调用是当前活跃期内最后一条待执行的语句,于是当这个调用返回时栈帧中并没有其他事情可做,因此也就没有保存栈帧的必要了。通过覆盖当前的栈帧而不是在其之上重新添加一个,这样所使用的栈空间就大大缩减了,这使得实际的运行效率会变得更高。

    def calc(n):
        print(n - 1)
        if n > -50:
            return calc(n-1)
    

      

     阶乘是尾递归么?

    def factorial(n):
        if n == 0: #是0的时候,就运算完了
            return 1
        return n * factorial(n-1) # 每次递归相乘,n值都较之前小1
     
    d = factorial(4)
    print(d)
    

    上面的这种递归计算最终的return操作是乘法操作。所以不是尾递归。因为每个活跃期的返回值都依赖于用n乘以下一个活跃期的返回值,因此每次调用产生的栈帧将不得不保存在栈上直到下一个子调用的返回值确定。

     参考文献:

    [1] 递归的精髓是什么?

    [2] 递归

     
     
  • 相关阅读:
    【BZOJ 2440】[中山市选2011]完全平方数
    【BZOJ 1066】[SCOI2007]蜥蜴
    luogu P1317 低洼地
    luogu P1379 八数码难题
    luogu P1886 滑动窗口
    luogu P1032 字串变换
    题解 P1876 【开灯】
    题解 P1720 【月落乌啼算钱】
    题解 P2863 【[USACO06JAN]牛的舞会The Cow Prom】
    关于线性回归
  • 原文地址:https://www.cnblogs.com/nxf-rabbit75/p/10138858.html
Copyright © 2011-2022 走看看