c印记(四): 递归

来源:互联网 发布:mmd模型动作数据 编辑:程序博客网 时间:2024/05/16 19:30

目录

    • 目录
    • 前因
    • 概述
    • 例子
    • 总结

1. 前因

前几天闲来无事就在csdn学院上转了转,然后看了一下尹成的c语言视频教程,虽然是讲的是c语言基础 ,但是讲的过程中会时不时的讲一些c语言的知识点对应到实际应用中是什么情况的。感觉还可以,于是乎就瞄了几眼,主要是看了递归,数组以及劫持这几块东西。说实话,虽然工作也有五六年了,但感觉在工作的工程中并未用到什么很”牛X”的算法,以前也没有怎么研究过递归这玩意儿。看了尹成讲的递归之后,感觉递归还是有点儿意思,所以就准备写点儿东西记录一下,不管是回顾也好,还是欣赏也罢。

2. 概述

基于以上的前因,顺播和度娘深度交流了一番,于是乎就有了这段记录。

咱首先还是教科书一点,将递归的概念摆出来(这里主要是指编程领域中的概念,百科上直接拿的):程序调用自身的编程技巧称为递归( recursion)。

知道这个概念之后,再进一步查询了一下,这递归还要分很多种类:
- 线性递归: 即一般型的递归,自身调用自身,这其中又可以分为以下几类:
- 直接递归: 函数A嵌套调用函数A自身
- 间接递归:函数A调用函数B,然后在函数B中再调用函数A
-

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

3. 例子

这里先不管线性递归还是尾递归,就列几个例子出来。
最简单的就拿个比较入门级的问题,从1加到100,即1+2+..+100,当然这里不会去使用n(n+1)/2这个公式,就是一般的累加实现,最直接的方法,也是初学时容易想到的方法——直接循环累加,其代码如下:

    int sum = 0;    int i;    for (i = 1; i <= 100; i++)    {        sum += i;    }

接下来,看看使用递归方式该如何实现,提到递归,就不得不说说数学归纳法了,其实从问题到解法实现的过程就是,首先使用数学归纳法分析并总结出一半规律和基本条件,然后再根据这个一般规律去
实现递归主体部分,而基本条件作为递归的结束条件。按照这个顺序,这里首先就是用数学归纳法来分析从1累加到一般的一般规律:

大家都知道1累加到100的结果是5050,这里:
5050 = 1+2+…+98+99+100//依次类推
4950 = 1+2+…+98+99
4851 = 1+2+…+98
由此可知,f(100) = 4950+100=f(99) + 100,同理,f(99) = 4851+99=f(98)+99,由此归纳可知f(n) = f(n-1)+n,当然这里是从最后的结论反向归纳的,也可以从正向归纳:
0+1 = 1
1+2 = 3
3+3 = 6
6+4 = 10
10+5 = 15,由此就有,
f(1) = 0+1 = f(0)+1,
f(2) = 1+2 = f(1)+2,
f(3) = 3+3 = f(2)+3,
f(4) = 6+4 = f(3)+4,
f(5) = 10+5 = f(4)+5,
也能得到f(n)=f(n-1)+n,其递归的结束条件这里就取 n = 0(当然也可以取n=100,只是递归的写法会 有些不太一样), 其源码如下:

int recursion_add(int n){    if (0 == n)//end condition    {        return n;     }    else    {        return recursion_add(n - 1) + n;    }}

以上就是一般递归的实现,在前面的概述中还提到了尾递归,算是对一般递归的一种优化实现,这里也
顺便将尾递归实现1累加到100的函数贴出来:

//这里就有两个参数,后面一个acc即是一个参数,也是用来存储结算结果的,调用//的时候,acc对应的实参应该为0,即 int sum = trail_recursion_add(100, 0)int trail_recursion_add(int n, int acc) {    if (0 == n)//end condition    {        return acc;     }    else    {        return recursion_add(n - 1, acc + n);    }}

还有一个比较有趣的例子,之所以有趣是因为,只需要稍微修改,其运行结果就是相反的,这个例子就是递归方式显示数组中的元素,一般的做法就是使用循环,其代码如下:

    int a[10] = {0,1,2,3,4,5,6,7,8,9};    int i;    for (i=0; i<10; i++)    {        printf("%d ", a[i]);    }

同样我们来看看递归方式的实现:

/** *这里以一个拥有10个元素的数组为例子,其调用方式为show(a, 10); */void show(int a[10], int n){    if (0 == n) //end condition,index range is: 0~9    {        return;    }    else    {          show(a, n - 1);        printf("%d ", a[n-1]); //先递归后打印,为顺序输出    }}

然后如果要逆序输出数组的元素的话,只需要将printf语句移动一下就可以了:

/** *这里以一个拥有10个元素的数组为例子,其调用方式为show(a, 10); */void show_reverse_order(int a[10], int n){    if (0 == n) //end condition,index range is: 0~9    {        return;    }    else    {          printf("%d ", a[n-1]); //先打印后递归,为逆序输出。        show(a, n - 1);    }}

4. 总结

递归的例子还有很多很多,比如计算阶乘,打印斐波那契数列,实现汉诺塔等等,这里就不再一一列举了。我觉得尹成在教程中说的好,递归是非常锻炼逻辑思维的,而且是非常直观的逻辑思维。咱们搞软件的就是需要比较严密的逻辑思维,在软件设计阶段就需要以直观的逻辑思维去对各种需求,约束条件等进行综合,然后得出方案,而不能一开始,还未得出初步的方案,就开始去考虑如何如何优化了,这样可能反而得不到较好的结果。

另外一点就是,我觉得,不管是初学者还是老鸟,都应该时不时的回顾一下一些基础知识,说不定就会有一些意想不到的收获,正所谓,温故而知新。

0 0