C语言 递归函数_递归函数c语言-程序员宅基地

技术标签: c语言  开发语言  

递归函数遵循1.函数直接或者间接调用自己,这样的函数我们把它称作递归的函数。

                      2.函数的退出条件。

                      3.形参上体现问题规模,而且问题规模一般为不断的缩小,直到达到函数退出条件。

递归的思想:

分解:大规模问题分解为小规模问题(问题规模缩小),直到小规模问题能够得到解决为止。

合并:将小规模的解逐层组合成原问题规模。

根据以上几点,我们抛出一个简单问题

使用递归求1+2+3+4?

#include<stdio.h>
int Getsum(int n)
{
    if(n == 1)
    {
        return 1;
    }
    return Getsum(n - 1) + n;
}
int main()
{
    int res = Getsum(4);
    printf("%d\n",res);
    return 0;
}

 过程如下:

右侧的箭头表示将n = 4带入,n不等于1,缩小规模直到达到退出条件。

左侧将值逐层返回至调用处。

接下来使用递归求斐波那契数列,求第n项的值。

以n = 5来看我们分析递归的过程

 当n = 6时,我们再分析一下:

 我们会发现,n仅仅加了1,通俗的说就是这个图烦琐了很多,如果n = 30呢?

实际上,像斐波那契数列这种递归形式的定义容易诱导人们使用递归形式来解决问题,程序如下所示:

#include<stdio.h>
int Fib(int n)
{
    if(n == 1 || n == 2)
    {
        return 1;
    }
    return Fib(n - 1) + Fib(n - 2);
}
int main()
{
    int res = Fib(5);
    printf("%d\n",res);
    return 0;
}

这里有一个陷阱:它使用递归步骤计算Fib(n-1)和Fib(n-2)。但是在Fib(n-1)时也将计算Fib(n-2)。这个额外的计算代价有多大呢?

答案是它的代价远远不止一个冗余计算——每个递归调用都触发另外两个递归调用,而这两个调用的任何一个还将触发两个递归调用,再接下去的调用也是如此。这样,冗余计算的数量增长的非常快。例如,在递归计算Fib(10)时,Fib(3)的值被计算了21次。但是,在递归计算Fib(30)时,Fib(3)的值被计算了317811次。当然,这317811次计算所产生的结果是完全一样的,除了其中之一外,其余纯属浪费。这个额外的开销真是相当的恐怖!

那么,对于该递归的缺陷,如果使用迭代呢?

迭代是使用一个简单的循环来代替递归。同样,这个循环形式不如递归符合前面斐波那契数列的抽象定义,但它的效率提高了几十万倍!

在使用递归方式实现一个函数之前,先问问自己使用递归带来的好处是否抵得上它的代价。而且必须小心:这个代价可能比初看上去要大得多!

用迭代方法解决斐波那契问题:

#include<stdio.h>
long Fib(int n)
{
    long res;
    long previous_res;
    long next_old_res;
    res = previous_res = 1;
    while(n > 2)
    {
        n -= 1;
        next_old_res = previous_res;
        previous_res = res;
        res = previous_res + next_old_res;
    }
    return res;
}
int main()
{
    int res = Fib(6);
    printf("%d",res);
    return 0;
}

再来一个使用递归解决二分查找问题

二分查找又称折半查找、二分搜索、折半搜索等,是在分治算法基础上设计出来的查找算法,对应的时间复杂度为O(logn)。

二分查找算法仅适用于有序序列,它只能用在升序序列或者降序序列中查找目标元素。

在有序序列中,使用二分查找算法搜索目标元素的核心思想是:不断地缩小搜索区域,降低查找目标元素的难度。

以在升序序列中查找目标元素为例,二分查找算法的实现思路是:

初始状态下,将整个序列作为搜索区域假设为 [A, E]);
找到搜索区域内的中间元素(假设所在位置为 M),和目标元素进行比对。如果相等,则搜索成功;如果中间元素大于目标元素,表明目标元素位于中间元素的左侧,将 [A, M-1] 作为新的搜素区域;反之,若中间元素小于目标元素,表明目标元素位于中间元素的右侧,将 [M+1, E] 作为新的搜素区域;
重复执行第二步,直至找到目标元素。如果搜索区域无法再缩小,且区域内不包含任何元素,表明整个序列中没有目标元素,查找失败

void Subblesort(int *arr, int len)
{
    for(int i = 0; i < len-1; i++)
    {
        for(int j = 0; j < len-1-i; j++)
        {
            if(arr[j] > arr[j+1])
            {
                int temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp; 
            }
        }
    }
}
int BinarySearch(int *arr, int len, int left, int right , int value)
{
    assert(arr != NULL && len > 0 && left > 0 && right > 0); 
    if(left > right || value < arr[left] || value > arr[right])
    {
        return -1;
    }
    int mid = (left+right) /2;
    int midVal = arr[mid];
    if(value > midVal)
    {
        return BinarySearch(arr,len,mid+1,right,value);
    }
    if(value < midVal)
    {
        return BinarySearch(arr,len,left,mid-1,value);
    }
    else
    {
        return mid;
    }
}

因为其仅使用于有序序列,以及我在本题中所遇到的问题(当数组内元素为0,1,2,3,4,5,6,7,10,8的顺序查找时,查找其中元素10,查找失败),我给其添加了一个冒泡排序,使其成为有序序列之后,再进行二分查找。
最后,我再说明一下函数的调用机制

Step1:建立(开辟)栈帧空间

Stap2:保护现场:主调函数运行状态(记录并存入栈帧空间) 入栈

Step3:形参进行存储空间开辟,形参 拷贝 实参,然后对函数的局部变量进行内存分配

Step4:开始执行函数体

Step5:释放被调用函数的栈空间

Step6:恢复现场:此时被调用函数已经执行完毕,返回至主调函数的地址,继续主调函数的后续语句执行
 

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/qq_46108282/article/details/126214518

智能推荐

iMeta | 宁波大学附属第一医院崔翰斌团队综述缺血性心脏病相关肠道微生物及菌群代谢物研究进展...-程序员宅基地

文章浏览阅读551次。点击蓝字 关注我们缺血性心脏病相关肠道微生物及菌群代谢物研究进展iMeta主页:http://www.imeta.science综 述●原文链接DOI: https://doi.org/10.1002/imt2.94● 2023年2月26日,宁波大学附属第一医院崔翰斌团队、浙江省动脉粥样硬化疾病精准医学研究重点实验室范勇团队在iMeta在线发表了题为“Microbiota-related ..._与急性心肌梗死有关的微生物

图形图形处理方面的一位微软专家的主页,_automated video looping with progressive dynamism-程序员宅基地

文章浏览阅读2.6k次,点赞2次,收藏5次。刚在Github上分享了一些不错的代码http://hhoppe.com/ Hugues Hoppe »DemosPublicationsTalksAcademicProfessionalMisc Hugues Hoppe [pronunciation]Principal researche_automated video looping with progressive dynamism

AOP实现权限拦截_apo拦截控制层-程序员宅基地

文章浏览阅读497次。AOP实现权限拦截注解名称:CheckUnSysAdmin注解实现类:CommonAspectController层方法上引入注解名称:CheckUnSysAdminpackage com.sf.XWFS.aop;import java.lang.annotation.*;/** * @author cc * Desc 校验除超管外的角色,都进行拦截 */@Documented@Retention(RetentionPolicy.RUNTIME)@Target(ElementType_apo拦截控制层

杀毒软件业野蛮生长法则:自己研发病毒自己杀-程序员宅基地

文章浏览阅读52次。时隔4个月后,瑞星杀毒造假案又有了戏剧性的变化。近日,瑞星杀毒造假案的主角——北京市公安局网监处原处长于兵的二审结果仍维持一审的死缓判决。而据于兵的最新供认资料,相当一部分病毒是杀毒软件公司自己的科技力量研制的。于兵供认,瑞星公司向其行贿时就提出条件,由公安机关发出病毒警报,提示用户下载该公司杀毒软件进行杀毒,而病毒则是由瑞星公司“研制”的。“其实这是杀毒软件行业里的公开秘密。”国内一家知名...

密码学考点整理_移位密码和vigenere密码的异同是什么-程序员宅基地

文章浏览阅读6k次,点赞4次,收藏35次。考试重点1. 密码体制分类对称密码体制和非对称密码体制;2. DES和AES算法的特点(结构、密钥长度,分组长度,DES弱密钥)及其过程(置换过程,S盒查表过程),AES的轮结构DESDES结构首先是一个初始置换IP,用于重排明文分组的64比特;相同功能的16轮变换,每轮都有置换和代换;第16轮的输出分为左右两半并被交换次序;最后经过一个逆初始置换产生64比特密文;DES结构图如下:密钥长度:56分组长度:64DES弱密钥:待续了解即可DES 分组长度_移位密码和vigenere密码的异同是什么

基于微信小程序+Springboot线上租房平台设计和实现【三端实现小程序+WEB响应式用户前端+后端管理】_微信小程序租房平台怎么弄-程序员宅基地

文章浏览阅读2.7w次,点赞97次,收藏158次。系统功能包括管理员服务端:首页、轮播图管理、公告信息管理、系统用户(管理员、租客用户、房主用户)资源管理(新闻列表、新闻分类列表)模块管理(房源信息、房源咨询、租赁申请、入住信息、房租信息、反馈信息、通知信息、房屋类型)个人管理;用户客户端:首页、公告信息、新闻资讯、房源信息等功能。_微信小程序租房平台怎么弄

随便推点

微信公众号网页静默授权/非静默授权(uniapp版)_微信公众号静默授权-程序员宅基地

文章浏览阅读7.7k次,点赞5次,收藏33次。一、问题为什么要进行网页授权?首先我们进行网页授权的需求是,获取用户信息、最主要是获取openid唯一值,可以用于用户登录、支付等功能,这时候就需要进行网页授权获取用户的信息以及openid。二、静默授权/非静默授权在操作之前可以先提前看看网页授权官方文档静默授权snsapi_base (不弹出授权页面,直接跳转,只能获取用户openid;用来获取进入页面的用户的openid的,并且自动跳转到回调页的。用户感知的就是直接进入了回调页(往往是业务页面)。非静默授权snsapi_user_微信公众号静默授权

A Key Volume Mining Deep Framework for Action Recognition-程序员宅基地

文章浏览阅读235次。A Key Volume Mining Deep Framework for Action Recognition_a key volume mining deep framework for action recognition

python创建窗体_python生成窗口-程序员宅基地

文章浏览阅读3.9k次。广告关闭腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!2、python生成目录树上述 cmd 方式虽然可以生成目录树,但是并不美观,让我们用 python 实现。 2.1 标准库pathlib介绍python有一个标准文件路径处理库 os.path ,从 python3.4 开始,python 又加入了一个标准库 pathlib ,该库..._python创建一个窗口

PowerDesigner16 时序图_使用powerdesiger 画出时序图有接口 控制-程序员宅基地

文章浏览阅读5.1k次,点赞5次,收藏10次。时序图(Sequence Diagram)是显示对象之间交互的图,这些对象是按时间顺序排列的。顺序图中显示的是参与交互的对象及其对象之间消息交互的顺序。时序图中包括的建模元素主要有:角色(Actor)、对象(Object)、生命线(Lifeline)、控制焦点(Focus of control)/ 激活(Activation)、消息(Message)、组合片段(Combined Fragments_使用powerdesiger 画出时序图有接口 控制

Doris系列17-动态分区_dynamic_partition.history_partition_num-程序员宅基地

文章浏览阅读1.2k次。文章目录一. 动态分区概述1.1 原理1.2 使用方式1.3 动态分区规则参数1.4 创建历史分区规则1.5 注意事项二. 案例2.1 案例12.2 案例22.3 案例3参考:一. 动态分区概述动态分区是在 Doris 0.12 版本中引入的新功能。旨在对表级别的分区实现生命周期管理(TTL),减少用户的使用负担。目前实现了动态添加分区及动态删除分区的功能。动态分区只支持 Range 分区。名词解释:FE:Frontend,Doris 的前端节点。负责元数据管理和请求接入。BE:Backend_dynamic_partition.history_partition_num

Linux命令_禅道的运行日志放在哪-程序员宅基地

文章浏览阅读309次。笔记_禅道的运行日志放在哪

推荐文章

热门文章

相关标签