算法设计 -- 递归

  • 递归概念
  • 与数学关系
  • 递归条件
  • 递归应用场景
  • 递归需要的关键部件
  • 递归的分类
    • 分类的角度
    • 尾递归
      • 尾递归的性质
  • 参考

递归概念


递归,是程序自己直接或者间接调用自己
作用:
1. 把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。
2. 减少程序代码量。
3. 容易阅读。

与数学关系

数学归纳
动力系统模型

递归条件


构成递归需具备的条件:(摘自 :百度百科)
1. 子问题须与原始问题为同样的事,且更为简单;【化简原则
2. 不能无限制地调用本身,须有个出口,化简为非递归状况处理。【终止原则

递归应用场景


递归算法一般用于解决三类问题(摘自 :百度百科):
(1)数据的定义是按递归定义的。(Fibonacci函数)
(2)问题解法按递归算法实现。
这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。
(3)数据的结构形式是按递归定义的。
如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。
图论:
树,图的遍历。
数学:
Fib(n) = (Fib(n-1) + Fib(n-2))

递归需要的关键部件


1. 边界条件 :对应前面提到的【终止原则】。(是控制前进,后退的 开关)
2. 递归前进段 : 体现了分解问题的过程。
3. 递归返回段小问题解决,支援大问题的过程。


代码实例:
阶乘
n! = n * (n-1) * (n-2) * …* 1(n>0)
相关说明:
1. 从中可以看出递归的各个组件。
2. 边界条件 丢失,导入程序无法终止。
3. 递归后退段没有,导致无法使用小问题解决大问题。
4. 递归前进段丢失,则 大问题无法进行分解。
5. 一次递归所含的工作量:下面这段代码,一次递归中只进行一次乘法运算,这从某种程度上来说,是对于递归的一种浪费。(因为入栈,出栈时间要多的多)
6. 递归灾难: 如果有1亿个数,那么这种做法递归层次将会达到1亿次。【所以为每次的递归合理分配工作量也是很重要的,确定一次递归处理单元的粒度】

int recursive(int i)
{
    int sum = 0;
    if (0 == i)  //1.终止条件
        return (1);//2.递归后退段。
    else
        sum = i * recursive(i-1);//3.递归前进段
    return sum;//2.递归后退段
}

递归的分类


事实上,我们当研究一个领域(比如说,递归)的时候,我们几乎总是可以找到某个分类的角度,然后将该类别/领域 进一步细分。递归也同样如此,接下来我们介绍一下分类的角度。

分类的角度


此处我们选取的分类的角度,是从递归发生的位置来进行划分的。为了理解该角度,让我们先来看一看递归的基本结构(如下面的代码)。此处我们先暂时忽略终止条件,然后只关注递归的代码,然后我们又可以将这些代码抽象为两类,发生递归的位置 以及不发生递归的位置
随后我们便可以根据相对位置来对递归进行分类了。

fun(){
    代码1;  //不发生递归的位置
    fun();  //发生递归的位置
    代码2;
}

尾递归


尾递归是按上面角度分类后,递归的一种特殊的类别。它的特点是递归发生的位置在最尾部。
百度百科中关于尾递归的定义:
当递归调用是整个函数体中最后执行的语句它的返回值不属于表达式的一部分时,这个递归调用就是尾递归。
形式如下:

fun(){
    代码1;  //不发生递归的位置
    代码2;
    fun();  //发生递归的位置
}

尾递归的性质

尾递归就是从最后开始计算, 每递归一次就算出相应的结果, 也就是说, 函数调用出现在调用者函数的尾部, 因为是尾部, 所以根本没有必要去保存任何局部变量. 直接让被调用的函数返回时越过调用者, 返回到调用者的调用者去.

其他的百度百科解释的很清楚。

参考

【百度百科】尾递归

你可能感兴趣的:(基本算法)