c印记(四): 递归

目录

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
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值