递归是数据结构中一种常用的算法,在非线性结构中,有相当多的问题都是使用递归的方法来解决,递归也是一种“很好用”的方法。
那么什么是递归呢?
递归就是将一个复杂的问题化成若干个简单的小问题,通过解决若干个小问题,在解决这整个大问题。通过函数自我调用过程(一个函数中使用“另一个函数”,那个函数就是自己本身的函数)先解决小问题中的“小问题”然后在解决整个小问题,最后解决整个大问题。小编对递归的理解是,先将一个大问题分成若干的小问题,在依次将若干个小问题继续分解成更“小”的问题,找到一个不能分解的最“小”的问题,并解决,最后依次解决各种小问题,最后解决大问题,这就是递归的全过程。
上面的讲述可能不太理解,那我们通过有关例子来解决。我们以最经典的递归例题,斐波那契数列为例。就是兔子数列。以a1为数列的首元素,已知第n个元素是第n-1个元素和第n-2个元素之和,而第一个和第二个元素为1。那我们如何求出第n个元素呢?
按照我们上面的说法,将“求出第n个元素看成是一个复杂的大问题”,我们将这大问题分解成两个小问题,分别是求出an-1和an-2,求出第an-1和an-2之后,就可以求出an,那我们又如何求出an-1和an-2呢?同样,将这两个小问题分解成跟小的问题,直到找出“最小的问题”,而这个最小的问题就是求出a1和a2。代码实现如下:
#include <stdio.h>
int ff(int n) //我们要求求出第n个元素
{
if(n==1||n==2) //如果n为1或者n为2这返回值为1;
return 1;
else //否则我们将问题分解,分解成两个小问题。调用自身函数来解决这些问题
return ff(n-1)+ff(n-2);
}
一些刚接触c语言的新手可能不容易看懂,我们使用图片来解决
如图,如果n为5,将求出a5分成求出a4和a3两个小问题,再将求出a4分成求出a3和a2两个小问题,在将求出a3分成求出a1和a2两个小问题,由于a1和a2等于1,我们就可以解决求出a4这个小问题,同理,在解决求出a3这个小问题,我们就解决求出a5这个大问题。
那么递归的结构如何设计呢?
首先,递归的结构有两个最重要的因素,递归体和递归出口。递归体就是分解问题的部分,将大问题分解成两个小问题,而递归出口就是定义最“小”的问题,最后解决最小的问题。递归出口不等于整个递归的结束,而是整个递归体的结束,整个递归体结束后在依次解决一些小问题,最后解决大问题,就算整个递归结束。
我们重新换一个结构为例来讲解,以求n的阶层来讲解:
首先n的阶层的定义就是从1开始一直乘到n,(1*2*3*4.......*n)若我们要求出n的阶层,将这个大问题化成一个小问题,就是先算出n-1的阶层,而要算出n-1的阶层就要先算出n-2的阶层。依次递归。而递归出口就是n为1的时候。代码实现如下。
#include <stdio.h>
int f1(int n)
{
if(n==1)
return 1; //递归出口
else
return n*f(n-1);//递归体
}
递归体相当于函数的自我调用过程,也是分解问题的过程。
递归在二叉树和一些算法中有着重要的应用,首先要理解递归,先要理解函数的自我调用。
这里留给大家几个问题:
1.递归有没有可能进入一个死循环
2.如何寻找递归出口和递归体
3.对于汉诺塔问题如何使用递归来解决。
递归的定义已经讲解完成,谢谢您的阅读。若有不足或者错误之处,欢迎在评论区指出,谢谢。