递归算法详解

递归算法

递归看这篇就够了

引子

我怀着无比激动的心情写下这篇文章,因为我它介绍是一种无比优雅的问题解决方案——递归



相信很多同学在学习递归的时候都会有这样一个疑惑,这个东西究竟是如何自己调用自己的,大佬写几行代码就实现了复杂的功能。今天我们就来好好讨论这个优雅的递归。

一、定义

在数学与计算机科学中,递归(Recursion)是指在函数的定义中使用函数自身的方法。实际上,递归,顾名思义,其包含了两个意思:递 和 归,这正是递归思想的精华所在。

用人话来说就是函数调用自己。

二、递归的三大要素

  • 明确这个函数作用 先不管代码,我们首先要明白的是我们要这个函数干什么。
    例如我定义一个函数
void f(int i){
  //计算阶乘
}

这个函数就是计算n的阶乘。好啦,现在我们已经明白了这个函数的作用,我们来看看第二要素吧。

  • 找到终止条件 我们需要给函数一个终止条件,总不能让函数一直在调用自己,在函数‘递’的这个过程结束后,拿着结果进行‘归’的过程。
//我们假设i=1
  void f(int i){ 
    if(i==1){
      return 1;
    }
  }

由上面的栗子我们可以直接看出当i=1时候,函数的返回值相信大家非常清楚。那我们现在来看看第三要素吧,

  • 提取重复的逻辑 不断的缩小范围,我们可以使用一些变量或操作使原函数结果不变。

    好啦,现在重要到第三步了,当然这一步也是有一丢丢难的,找不清等价关系式没关系,因为你不是天才,你需要多接触几道题,我在后面也会增加10道递归算法题的。话不多说我们看代码。

   void f(int i){
     if(i==1) //递归结束的条件
       return 1; 
    //函数的等价操作
     return f(i-1)*i;
   } 

f(i-1)就是函数的等价式,我们通过不断缩小i的值,这样范围就变小了。为了不改变原函数的结果,我们还需要进行*i。

三、递归执行

看到这里,我相信大家对于递归的定义和递归的要素都已经很清楚,那么,我们接下来我们就来谈谈递归的执行吧。

递归程序的运行过程可以分为两个阶段

  • 递推阶段 递归每一次都是基于上一次进行下一次的执行。
  • 回溯阶段 当遇到终止条件,则从最后往回一级一级的把值返回来。

我们还是以求i的阶乘为例子吧。

void func(int i){
  if(i==1) //递归结束条件
    return 1;
  return f(i-1)*i; //关系等价式
}

通过画图我们可以清楚的看到递归的运行过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7JRynTKq-1607070567913)(https://imgkr2.cn-bj.ufileos.com/892f8e2f-bec1-4501-95ad-1e7ab2d9cbcc.png?UCloudPublicKey=TOKEN_8d8b72be-579a-4e83-bfd0-5f6ce1546f13&Signature=waZiwh%252FiYuCsKWrIzPJKX4xNO8c%253D&Expires=1607147707)]
通过看图我们不难发现func()函数一直在调用自己,当i=1时,递推结束,函数开始一层一层回溯。所以在使用递归来解决问题时候我们一定要注意递归结束的条件,如果没有结束条件的话,无限递归会造成的堆栈溢出的。

四、经典递归问题实战

(1)斐波拉契数列

斐波纳契数列,又称黄金分割数列,指的是这样一个数列:1、1、2、3、5、8、13、21、……

这里我们使用两种方法来解决这个问题,通过这两种方式,我想大家能够感受到递归的妙处。

非递归实现方法

  //非递归实现
  int func(int n){
    if(n==1||n==2){
        return 1;
    }
    int result = -1;
    int first = 1;  
    int second = 1;
    for (int i = 3; i <= n; i++) { // 循环
       
        result = first + second;
        first = second;
        second = result;
    }
    return result;
}

递归实现

int func(int n){
    if(n==1||n==2){ //递归出口
        return 1;
    }
    return func(n-1)+func(n-2);
}

我们不难发现使用使用递归的代码比非递归的代码要简洁的多,同时也能看出递归的代码我们通过循环也能完成。现在引出了我们的下一个问题。

递归和循环我们应该怎么选择?

在我看来,递归和循环解决问题都是一样的,递归能使解决方案更加清晰,但是递归在性能上没有优势。实际上,在开发中,有些情况使用循环比递归显得更好。如果使用循环,程序的性能将会更好;但是使用递归,程序将会更加容易理解。如何选择看什么对你更加重要

好啦!今天的文章就到这里啦,这篇文章上个礼拜就开始写了,奈何最近考试太多了。好在,复习了一个礼拜,感觉自己答的还是可以的。看来考前突击很重要的(敲重点)。今天难得有空,终于把文章写完了。如有错误欢迎大家指出。如果本篇文章对你有收获,那么就点个再看再走吧。

作者 :Arleny
CSDN : Keiy_


点个关注在走吧,我们下期再见。
在这里插入图片描述


  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值