• 【经典算法】递归解析


      在非负整数集上定义一个函数f,它满足f(0)=0,且f(x)=2f(x-1)+x^2.从这个定义可以看出f(1)=1,f(2)=6,f(3)=21,f(4)=58。当一个函数用自身定义时就称为递归(recursive).即,一个函数直接或间接地调用自身,是为直接或间接递归。C++是允许递归的。但必须记住,C++所做的仅仅是试图遵循递归的思想。不是所有的数学递归函数都能有效的用C++递归模拟来实现。要点在于,递归函数f应该像非递归函数一样只用几行代码就能表示出来。下图给出了函数f的递归实现。

    1 int f(int x) {
    2     if (x == 0)
    3         return 0;
    4     else
    5         return 2 * f(x - 1) + x * x;
    6 }

    第2行和第3行处理基准情况(base case),即此时函数的值可以直接算出来而不用递归。正如在没有f(0)=0的前提下。声称f(x) = 2f(x - 1) + x^2.在数学上没有意义一样。C++的递归方法若无基准情况也是毫无意义的。第5行执行的是递归调用。

      编写递归程序的时候,关键是要牢记递归的四条基本法则:

    1. 基准情形。 必须有某些基准情形不用递归就能求解。
    2. 不断推进。 对于那些需要递归求解的情形。递归调用必须总能朝着基准情形的方向迈进。
    3. 设计法则。 假设所有的递归调用都能运行。
    4. 合成效益法则。 在求解一个问题的同一实例时,切勿在不同的递归调动中做重复性的工作。(摊还分析)

    递归和循环

      如果我们要重复地多次计算相同的问题,通常可以选择用递归或者循环两种不同的方法。递归是在一个函数的内部调用这个函数自身。而循环这是通过设置计算的初始值及终止条件,在一个范围内重复计算。比如求1+2+3+...+n,我们可以用递归或者循环两种方式求出结果。对应的代码如下:

      

    int AddFrom1ToN_Recursive(int n) {
    	return n <= 0 ? n + AddFrom1ToN_Recursive(n - 1);
    }
    
    int AddFrom1ToN_Iternative(int n) {
    	int result = 0;
    	for (int i = 0; i <= n; ++i)
    		result += i;
    
    	return result;
    
    }
    

      通常递归的代码比较简洁。在上面的例子中,递归的代码只有一个语句。而循环的则需要四个语句。在树的前序、中序、后序遍历算法的代码中,递归的实现明显要比循环简单的多。

      面试小提示:

        通常基于递归的代码要比基于循环实现的代码要简洁很多,更加容易实现。如果面试官没有特殊要求,应聘者可以优先采用递归的方法编程。

      递归虽然有简洁的优点,但它同时也有显著的缺点。

      递归由于是函数调用自身,而函数调用是有空间和时间的消耗的:每一次函数调用,都需要在内存栈中分配空间以保存参数、返回的地址及临时变量,而且往栈里压入数据和弹出数据都需要时间。这就不难理解上述的例子中递归实现的效率不如循环。

      另外,递归中有可能很多计算都是重复的,从而对性能带来很大的负面影响。递归的本质是把一个问题分解成两个或多个小问题。如果多个小问题存在相互重叠的部分,那么就存在重复的计算。

      除了效率以外,递归还有可能引起更严重的问题:调用栈溢出。前面分析中提到需要为每一次函数调用在内存栈中分配空间,而每个进程的栈的容量是有限的。当递归调用的层级太多时,就会超出栈的容量,从而导致栈溢出。在上述的例子中,如果输入的参数比较小,如10,它们都能返回结果55.但如果输入的参数很大,比如5000,那么递归代码在运行的时间就会出错,但运行循环的代码能得到正确的结果12502500.

     相关资料:

      1. http://community.topcoder.com/tc?module=Static&d1=tutorials&d2=recursionPt1

      2. http://www.nowamagic.net/librarys/veda/detail/2314

    参考资料:

      1. 《数据结构与算法分析C++描述》 Mark Allen Weiss

      2. 《剑指offer》 何海涛

  • 相关阅读:
    20172327 2018-2019-1 《程序设计与数据结构》第二周学习总结
    20172327 2018-2019-1 《程序设计与数据结构》第一周学习总结
    20172327 2018-2019-1 《第一行代码Android》第二章学习总结
    20172327 2018-2019-1 《第一行代码Android》第一章学习总结
    学号 2017-2018-2《程序设计与数据结构》课程总结
    20172327 2017-2018-2 《程序设计与数据结构》实验5报告
    Django框架(五)-- 视图层:HttpRequest、HTTPResponse、JsonResponse、CBV和FBV、文件上传
    Django框架(四)-- 路由控制:有名/无名分组、反向解析、路由分发、名称空间、伪静态、APPEND_SLASH、不同版本的Django区别、Django虚拟环境搭建
    Django框架(三)-- orm增删改查、Django生命周期
    Django框架(二)-- 基本配置:app注册、模板配置、静态文件配置、数据库连接配置post和get
  • 原文地址:https://www.cnblogs.com/vincently/p/4191734.html
Copyright © 2020-2023  润新知