文章目录

  • 递归的定义和必要条件
  • 初步了解
    • 打印一个数的每一位
    • 计算一个数的每位之和
    • 计算一个数的阶乘
  • 进阶探讨
    • 二分查找法递归实现
    • 递归实现字符串函数strlen
    • 递归实现字符串逆序
  • 总结

递归的定义和必要条件

很多人提到递归就不由自主的想到了“套娃“二字,确实递归要函数一层一层地开辟,是非常绕的。那么想要领悟到递归的精髓我们就要先了解递归的定义和递归的必要条件。

定义:
程序调用自身的编程技巧称为递归( recursion)。 递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。 递归的主要思考方式在于:把大事化小

必要条件:
1、存在限制条件,当满足这个限制条件的时候,递归便不再继续。
2、每次递归调用之后越来越接近这个限制条件。
当我们理解递归存在的必要条件后,我们要开始实战了,通过具体的问题来使用递归来解决,这样才能快速掌握。毕竟实践是检验真理的唯一标准。

初步了解

打印一个数的每一位

递归方式实现打印一个整数的每一位,如1357,我们要打印出1 3 5 7这四个数。
我们就可以想到用取余和取模来分开数字,
1357 % 10 - - - - - - -7 得到数字7
1357 / 1 - - - - - - -135

135 % 10 - - - - - - 5 得到数字5
135 / 10 - - - - - - -13

13 % 10 - - - - - - - 3 得到数字3
13 / 10 - - - - - - - -1

1 % 10 - - - - - - - -1 得到数字1
代码如下:

#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>void print(int sum)
{if (sum >= 10){print(sum / 10);}printf("%d ", sum % 10);
}int main()
{int sum = 0;printf("请输入一个数字:");scanf("%d",&sum);print(sum);printf("\n");return 0;
}

可能一些朋友看到这里还是有点不太理解,我画了函数递归开辟的形象图有助于理解,图片如下:

上面的分析我们用到了递归的定义和必要条件,即递归存在限制条件,如我们的
if(sum>=10)在这个范围内就一直开辟函数递归,等不满足限制条件后再一层层地返回去。
运行结果如下:

计算一个数的每位之和

递归方式实现计算一个数的每位之和,如1234,计算出1+2+3+4=10。如2468,要计算出2+4+6+8=20。
这个和我们上面的打印一个数的每一位是类似的,异曲同工。我们也用取余和取模从而得到1 2 3 4这四个数字。我们先得到4,接着是3,然后再是2,最后数字1不满足限制条件就一层层返回直到结束。
代码如下:


#include <stdio.h>int fun(int sum)
{if (sum >= 10){return sum % 10 + fun(sum / 10);}else{return sum;}}
int main()
{int sum = 0;printf("请输入一个数字:");scanf("%d",&sum);printf("%d ", fun(sum));return 0;
}

函数递归开辟流程图如下:

程序运行结果:

计算一个数的阶乘

比如要计算5的阶乘,那就是5 * 4 * 3 * 2 * 1=120。
所以我们阔以大事化小,先得到数字5,然后是4,接着是3,再接着是2,最后是1,所以可以用递归来实现。我们只要把握住开辟下一个函数时传过去的数字自减1即可。
代码如下:

#include <stdio.h>int fun(unsigned int sum)
{if (sum > 1){return sum * fun(sum - 1);}else{return 1;}
}
int main()
{unsigned int sum = 0;printf("请输入一个数字:");scanf("%d",&sum);printf("阶乘为:%ld ", fun(sum));return 0;
}

函数递归开辟流程图如下:

上面就是函数递归的真面目,递归是自己调用自己,但不是停留在一个函数上,而是一直开辟相同的函数,直到不能满足限制条件。

进阶探讨

二分查找法递归实现

二分查找法也被称为折半法,主要不断缩小半个区域,一直找下去。
如在 1 2 3 4 5 6 7 8 9 10中找数字7,
发现7比5大,查找空间变为:6 7 8 9 10;
发现7比8小,查找空间变为:6 7 ;
发现7比6大,查找空间变为:7 ;
最后发现就是7,则找到了。

所以思路就明白了,递归和非递归写法如下:
递归法

#include <stdio.h>//不带返回值写法void fun1(int* p, int left, int right,int k)
{if (left <= right){int mid = (left + right) / 2;if (k < p[mid]){right = mid - 1;fun(p, left, right, k);} else if (k > p[mid]){left = mid + 1;fun(p, left, right, k);}else{printf("找到了,下标是:%d\n",mid);}}else{printf("找不到\n");}
}//带有返回值写法
int fun(int* p, int left, int right, int k)
{if (left <= right){int mid = (left + right) / 2;if (k < p[mid]){right = mid - 1;return fun(p, left, right, k);}else if (k > p[mid]){left = mid + 1;return fun(p, left, right, k);}else{return 1;}}else{return 0;}
}int main()
{int arr[10] = { 98,0,1,2,3,4,5,6,7,8};int k = 5;int left = 0;//把最后一个元素下标赋值给rightint right = sizeof(arr) / sizeof(arr[0]) - 1;int ret = Binary_Search(arr, left, right, k);if (ret!=0){printf("找到了\n");}else{printf("找不到\n");}return 0;
}

非递归法:

// *****二分查找
#include <stdio.h>
int main()
{int k = 88; //定义要找的数字int arr[] = { 11,22,33,44,55,66,77,88,99,100};int sz = sizeof(arr) / sizeof(arr[0]);int left = 0;int right = sz - 1;int mid = 0;while (left <= right){mid = (left + right) / 2;if (arr[mid] > k){right = mid - 1;}else if (arr[mid] < k){left = mid + 1;}else{printf("找到了,下标为:%d \n",mid);break;}}if (right < left){printf("找不到数字\n");}return 0;
}

递归实现字符串函数strlen

我们平常求字符串长度的时候,用的库函数就是strlen(),那我们也模拟实现一下。
代码如下:

#include <stdio.h>//  迭代方式
int my_strlen1(char* p)
{int count = 0;while (*p != '\0'){count++;p++;}return count;
}//  递归方式
int my_strlen2(char* p)
{if (*p != '\0'){return 1 + my_strlen2(p + 1);}else{return 0;}
}int main()
{char* p = "abcdefg";int ret1 = my_strlen1(p);int ret2 = my_strlen2(p);printf("递归方式结果: %d \n",ret1);printf("迭代方式结果: %d \n", ret2);
}

有兴趣的朋友也可以画一下递归函数的图片来分析分析,我这里就不放了哈。

程序执行结果:

递归实现字符串逆序

字符串逆序顾名思义就是把字符串给颠倒过来,比如 “abcdef” 变成 “fedcba” 。
我们设两个变量,最左边为 left ,右边为 right ,只要把左边和右边互换就可以了,
然后 左边往右边移动 ,右边往左边移动 直到变量 left大于 right 。
所以思路分析完毕,字符串逆序也是能够很好体现递归的例子,也是利用大事化小。
代码如下:

 #include <stdio.h>int my_strlen(char* p)
{if (*p != '\0'){return 1 + my_strlen(p + 1);}else{return 0;}
}//  递归方式写法
int Reverse(char* p, int left, int right)
{if (left > right){return 0;}else{char tmp = p[left];p[left] = p[right];p[right] = tmp;return Reverse(p, left + 1, right - 1);}
}//非递归写法
void  Reverse2(char* p, int left, int right)
{while (left < right){char tmp = p[left];p[left] = p[right];p[right] = tmp;left++;right--;}
}int main()
{char arr[] = "abcdef";int len = my_strlen(arr);int left = 0;int right = len - 1;Reverse(arr,left,right);printf("反转结果为:\n");printf("%s",arr);printf("\n\n");return 0;
}

程序执行结果为:

总结

  1. 许多问题是以递归的形式进行解释的,这只是因为它比非递归的形式更为清晰。
  2. 但是这些问题的迭代实现往往比递归实现效率更高,虽然代码的可读性稍微差些。
  3. 当一个问题相当复杂,难以用迭代实现时,此时递归实现的简洁性便可以补偿它所带来的运行时开销。

欢迎大家在评论区留言和探讨,大家一起进步,顺便点个赞呗!

递归很套娃怎么办?来,这篇文章带你快速领悟递归的奥义相关推荐

  1. 四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding

    文章目录 四篇文章带你快速入门Jetpck(中)之ViewModel,DataBinding Jetpack 官方推荐架构 ViewModel 添加依赖 创建ViewModel 初始化ViewMode ...

  2. 每个成功的男人背后都有个厉害的女人,这篇文章带你看看互联网大佬们背后的女人。...

    每个成功的男人背后都有个厉害的女人,这篇文章带你看看互联网大佬们背后的女人. 别看这些互联网大佬现在一幅高大上的样子,可是别忘了人家也有屌丝的时候,也有青春少年的时候啊!今天咱们就八卦一下,翻翻这些大 ...

  3. 设计模式一网打尽,40余篇文章带你领略设计模式之美

    文章末尾附带GitHub开源下载地址. 该文章的最新版本已迁移至个人博客[比特飞],单击链接 设计模式一网打尽,40余篇文章带你领略设计模式之美 | .Net中文网 访问. 设计模式概述 20世纪80 ...

  4. 什么产品适合抖音广告?本篇文章带你来了解

    到底什么产品适合抖音广告?抖音广告类型的多样化使不计其数的广告主选择在抖音这个热门app上投放产品广告.不断有很多广告主提出疑问,自己的产品到底适不适合在抖音做营销.下面为您介绍适合在抖音做广告的产品 ...

  5. 好多人都说存储过程很难?认真看这篇文章就够了

    何为存储过程? 存储过程是在数据库管理系统中保存的.预先编译的并能实现某种功能的sql程序,说直白点,java知道吧?和java的方法一样. 每遇到一个新的知识点时,我们都会看看它的优点,从而加深对它 ...

  6. synchronized()_这篇文章带你彻底理解synchronized关键字

    Synchronized关键字一直是工作和面试中的重点.这篇文章准备彻彻底底的从基础使用到原理缺陷等各个方面来一个分析,这篇文章由于篇幅比较长,但是如果你有时间和耐心,相信会有一个比较大的收获,所以, ...

  7. 实战| Python爬虫、构建GUI、程序打包,这篇文章带你玩过瘾!

    系列导读 01.PySimpleGUI|基础 02.PySimpleGUI|进阶 大家好,在之前的PySimpleGUI基础与进阶文章中,我们已经介绍了如何使用它构建GUI的基本方法,本文将进一步通过 ...

  8. 两篇文章带你走入.NET Core 世界:CentOS+Kestrel+Ngnix 虚拟机先走一遍(一)

    背景: 上一篇:ASP.Net Core on Linux (CentOS7) 共享第三方依赖库部署 已经交待了背景,这篇就省下背景了. 折腾的过程分两步: 第一步是:本机跑虚拟机部署试一下: 第二步 ...

  9. 12篇文章带你进入NLP领域,掌握核心知识

    专栏<NLP>第一阶段正式完结了.在本专栏中,我们从NLP中常用的机器学习算法开始,介绍了NLP中常用的算法和模型:从朴素贝叶斯讲到XLnet,特征抽取器从RNN讲到transformer ...

最新文章

  1. 学习笔记TF064:TensorFlow Kubernetes
  2. 前向业务中间层的意义,架构,技能要求
  3. xfce4终端的字体颜色修改
  4. [Silverlight入门系列]使用MVVM模式(3):Model的INotifyPropertyChanged接口实现
  5. WPF之无法触发KeyDown或者KeyUp键盘事件
  6. mysql字符串和数字的互相转换
  7. 【HTML】简单实现网页加载动画
  8. CNN笔记:通俗理解卷积神经网络--理解不同输入通道和卷积核通道关系(红色部分)
  9. Java 发起http GET POST请求实例
  10. hhvm php5.6,PHP_5.5_/_PHP5.6_/_PHP-NG_和_HHVM_哪个性能更好?
  11. 中国宽带最新速率状况报告 你家达标了吗?
  12. DataTable 去重合并
  13. 性能测试20--Analysis -- 内存与硬盘
  14. ElasticSearch搜索引擎搭建笔记
  15. R7 5800H 和 R5 5600H的差距大吗 哪个好
  16. 一个定语修饰两个并列的名词。
  17. ​巴比特发布2020年数据报告:平台年度阅读量突破16亿,活跃作者超500名
  18. python ImportError: cannot import name ' ×××'解决方法
  19. 数据结构之KH[第七章] -->选择题 (二)
  20. 频响测试低12dB问题

热门文章

  1. windows server 2012 计划任务 系统找不到指定文件
  2. Filling in the gaps—floating-point numbers
  3. 我是如何成为阿里巴巴数据分析师的?
  4. 毕业设计 嵌入式 RFID智能门禁系统
  5. 2021年高处安装、维护、拆除考试题及高处安装、维护、拆除考试题库
  6. 拉绳位移传感器测量位移是如何做到的?
  7. 编程之美 扩展问题 之 如何处理二维空间的覆盖问题。
  8. pytorch安装(cpu版本)
  9. 手把手教你做跳一跳助手
  10. C语言实现Base64编码解码