文章目录

  • 函数递归
    • 函数递归的定义和优缺点
    • 递归的使用场景及必要条件
    • 递归的细节说明
    • 递归的习题讲解
      • 1打印整数每一位
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 2递归和非递归求n阶乘
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 3`strlen`函数模拟
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 4逆序字符串
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 5递归实现数字各位之和
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 6求n的k次幂
        • 输入输出示例
        • 解题思路
        • 代码逻辑
      • 7递归求斐波那契数列
        • 输入输出示例
        • 解题思路
        • 代码逻辑
    • 经典问题
      • 汉诺塔问题
        • 游戏规则
        • 解题思路
      • 青蛙跳台阶
        • 游戏规则
        • 解题思路

函数递归

函数递归的定义和优缺点

程序调用自身的行为就是递归。可以直接或间接的调用,本质是把复杂的问题转化为一个规模小的问题。递归一般只需少量的代码就可描绘出多次重复计算。其主要思考方式在于大事化小

优点是为具有某些特征的编程问题提供了最简单的策略,缺点是层层调用,算法的复杂度可能过高,以致于快速耗干了计算机的内存资源,不方便阅读和维护等。

递归的使用场景及必要条件

使用场景

  1. 能够要求转化为新的问题,且二者解决方法相同,所处理的对象存在规律变化。
  2. 非递归比较麻烦,而递归很简单。
  3. 有模板或是公式可以直接套用,不会出现明显问题。

必要条件

  • 明确存在限制条件
  • 每次递归越来越逼近条件

递归的细节说明

  • 每级递归都有自己的变量,可能名称相同,但是其值不同。

    递归调用时,系统自动保留当前函数的参数变量。每次调用系统都会为函数开辟相应的空间。

  • 每次调用都要返回值,递归执行结束后,控制权传回到上一级函数。

    调用结束后,系统释放本次调用所开辟的空间,程序返回到上一次的调用点,同时获得初进该级调用的参数。

    每级递归必须逐级返回,不可跳跃或间断。

  • 函数中递归语句之前的代码,按被调函数的顺序执行,递归之后的代码,与被调函数相反的顺序执行。

递归的习题讲解

1打印整数每一位

用递归的方式,实现打印一个整数的每一位的功能。

输入输出示例

输入:1234

输出:1 2 3 4

解题思路

print(1234)
=== print(123)+4
=== print(12)+3+4
=== print(1)+2+3+4
=== printf(1)+2+3+4

这便是前面使用场景中所写的,将题目要求问题转化为新的问题,且变量有规律的变化

代码逻辑

n是不是个位数,递推调用n / 10

n是个位数,回归打印n % 10

void Print(int n)
{if (n > 9){Print(n / 10);}printf("%d ", n%10);
}
int main()
{int num = 0;scanf("%d", &num);Print(num);   return 0;
}
2递归和非递归求n阶乘

用递归和非递归的方法,分别实现求n的阶乘的功能(不考虑溢出)。

输入输出示例

输入:5

输出:120

解题思路

n∗n−1∗n−2∗n−3∗…∗1n*n-1*n-2*n-3*…*1n∗n−1∗n−2∗n−3∗…∗1

代码逻辑

fac(n)=n∗fac(n−1),n>0fac(n) = n * fac(n-1) , n>0fac(n)=n∗fac(n−1),n>0

fac(n)=1,n=0fac(n) = 1 , n=0fac(n)=1,n=0

int fac(int n)//非递归
{int ret = 1;for (int i = 1; i <= n; i++){ret *= i;}return ret;
}
int fac(int n)//递归
{if (n > 0)return n * fac2(n - 1);elsereturn 1;
}
int main()
{int n = 0;scanf("%d", &n);  printf("%d\n", fac(n));return 0;
}
3strlen函数模拟
输入输出示例

输入:abcdef

输出:6

解题思路

strlen(abcdef\0)
1+strlen(bcdef\0)
1+1+strlen(cdef\0)
1+1+1+strlen(def\0)
1+1+1+1+strlen(ef\0)
1+1+1+1+1+strlen(f\0)
1+1+1+1+1+1+strlen(\0)

代码逻辑

若∗ch≠0,strlen(arr)=1+strlen(arr+1)若 *ch≠0 , strlen(arr) = 1 + strlen(arr+1)若∗ch​=0,strlen(arr)=1+strlen(arr+1)
若∗ch=0,strlen(arr)=0若*ch=0 , strlen(arr) = 0若∗ch=0,strlen(arr)=0

int my_strlen(char* ch)
{if (*ch != '\0'){return 1 + my_strlen(ch + 1);}return 0;
}
int main()
{char ch[20] = { 0 };scanf("%s", &ch);printf("%d", my_strlen(ch));return 0;
}
4逆序字符串

不开辟额外空间的情况下,不使用字符串库函数,递归实现字符串反向排列,而不是倒序打印。

输入输出示例

输入:abcdef

输出:fedcba

解题思路

abcdef

递推:(先把后面赋值给前面,后面用覆盖\0)

$ \Rightarrow$ f b c d e \0

⇒\Rightarrow⇒ f e c \0\0

⇒\Rightarrow⇒ f e d \0\0\0

回归:(把前面转移出去的字符对应赋值给\0)

$ \Rightarrow$ f e d c \0\0

⇒\Rightarrow⇒ f e d c b \0

⇒\Rightarrow⇒ f e d c b a

代码逻辑

reverse("abcdef\0") 交换a和f+reverse("f bcde\0\0") 交换a和f+交换b和e+reverse("fe cd\0\0\0") 交换a和f+交换b和e+交换c和d+reverse("fed \0\0\0\0")

  • 交换两个字符

    1. 将在前的字符先放到一边存着
    2. 把在后的字符赋值到前面的位置
    3. 再把后面的位置对应覆盖为\0
  • 原在前字符替换\0
    1. 把事先存好的在前的字符对应替换到\0的位置上

void reserve_string1(char* ch)//指针
{char* left = ch;char* right = ch + strlen(ch) - 1;while (left < right){char tmp = *left;//不能交换地址,只能交换内容*left = *right;*right = tmp;left++;right--;}
}
void reserve_string2(char* ch)//数组
{int left = 0;int right = strlen(ch) - 1;while (left < right){char tmp = ch[right];ch[right] = ch[left];ch[left] = tmp;left++;right--;}
}void reverse_string3(char* ch)//递归
{char* left = ch;char* right = ch + strlen(ch) - 1;if (*ch != '\0'){char tmp = *left;//提取*left = *right;//赋值*right = '\0';//赋\0reverse_string3(ch+1);//ch+1,而不是ch++*right = tmp;//赋值}
}
int main()
{char ch[20] = "abcdef";//char* ch = "abcdef";//err - 常量字符串不可修改reverse_string3(ch);printf("%s\n", ch);return 0;
}
5递归实现数字各位之和

写一个递归函数DigitSum(),输入一个非负整数,返回组成它的数字之和

输入输出示例

输入:1234

输出:10

解题思路

1234
DigitSum(123)+4
DigitSum(12)+3+4
DigitSum(1)+2+3+4

1+2+3+4

1234%10=4
1234/10=123

123%10=3
123/10=12

12%10=2
12/10=1

1%10=1
1/10=0

一个数模10得到尾数,除10得到尾数前面的数字

通过不断的除10模10,就可以把每一位数字放到末尾,从而得到每一位数字

代码逻辑

若n不为个位数,先%10得到尾数,再/10

一定要有递归的出口,即当n为个位数时,函数返回n

int DigitSum(int n)
{if (n > 9)return DigitSum(n / 10) + n % 10;elsereturn n;//递归的出口
}
int main()
{int n = 0;scanf("%d", &n);printf("%d\n", DigitSum(n));return 0;
}
6求n的k次幂

输入两个整数分别代表底数和次幂,递归实现n的k次幂的功能。

输入输出示例

输入:2 3

输出:8

解题思路

k>0时,函数返回n*power(n,k-1)

k=0时,函数返回1,这是程序的出口,是程序递归到最后必须要计算的值

代码逻辑

nk=n∗nk−1,k>0n^k = n * n^{k-1} ,k > 0nk=n∗nk−1,k>0
nk=1,k=0n^k = 1 , k = 0nk=1,k=0

double power(int n,int k)
{if (k > 0)return n * power(n, k - 1);else if (k == 0)return 1.0;//递归的出口k=0elsereturn 1.0 / power(n, -k);
}
int main()
{int n = 0;int k = 0;scanf("%d%d", &n, &k);printf("%lf\n", power(n, k));return 0;
}
7递归求斐波那契数列

递归和非递归分别实现求第n个斐波那契数

输入输出示例

输入:5

输出:5

解题思路

1123581321345589...1\quad 1\quad 2\quad 3\quad 5\quad 8\quad 13\quad 21\quad 34\quad 55\quad 89\quad ...1123581321345589...

代码逻辑

递归:

Fib(n)=Fib(n−1)+Fib(n−2),n>2Fib(n) = Fib(n-1) + Fib(n-2) , n>2Fib(n)=Fib(n−1)+Fib(n−2),n>2
Fib(1)=Fib(2)=1Fib(1) = Fib(2) = 1Fib(1)=Fib(2)=1

非递归:

上一次的b换成这一次的a

上一次的c换成这一次的b

如此循环,就可以从前往后一个一个求。

int Fib(int n)
{if (n > 2)return Fib(n - 1) + Fib(n - 2);elsereturn 1;
}

但是这个方法效率是非常低的,当数字特别大时,层层拆分下来,时间效率是O(2n)O(2^n)O(2n)。

根据公式可知,第三个斐波那契数可由前两个得到,我们利用这个规律

int Fib(int n)
{if (n <= 2)return 1;int a = 1;int b = 1;int c = 1;//n=3时不用运算while (n >= 3)//从头开始移动n-2次,n=3时不用{c = a + b;a = b;//b赋值给ab = c;//c赋值给b     n--;}return c;
}int main()
{int n = 0;scanf("%d", &n);printf("%d",Fib(n));return 0;
}

经典问题

汉诺塔问题

汉诺塔,小时候游戏机上经常看别人玩的,自己玩到三四局就玩不下去了的那款游戏。当然如果你觉得非常简单,小时候能玩的行云流水,那你有本事到我面前说,礼貌谢谢(狗头保命)。

游戏规则

有三根柱子,分别为A、B、C ,A柱上从上到下依次排列着由小到大的圆盘,我们需要把圆盘从A柱按照同样的摆放顺序放到C柱上,期间我们可以借助B柱。

  • 每次只能挪动一个且是最上面的圆盘
  • 按照从上到下依次是由小到大的顺序摆放。
解题思路

假设由N个盘子,只需要考虑第NNN个盘子和其上N−1N-1N−1个盘子的整体。显然思路就是,第NNN个是要放在CCC柱上的,

  1. 首先将N−1N-1N−1个整体是先放在BBB柱上;
  2. 其次将第NNN个放在CCC柱上;
  3. 最后将N−1N-1N−1个整体放到CCC柱上。

即:第NNN个A→BA\rightarrow BA→B,N−1N-1N−1个整体A→B→CA\rightarrow B\rightarrow CA→B→C 。然后再考虑N−1N-1N−1个中把第N−1N-1N−1个当作最后一个,其上N−2N-2N−2个当作整体,到最后只剩一个直接放到CCC柱上。这便是递归的整体思路。

void move(int n, int x, int z)
{printf("%d盘:%c->%c\n", n, x, z);
}
void hannoi(int n, char x, char y, char z)
{if (n == 1)move(n, x, z);else{hannoi(n - 1, x, z, y);move(n, x, z);hannoi(n - 1, y, x, z);}
}
int main()
{int input = 0;do {printf("输入盘数开始测试(0. 退出测试)\n");scanf("%d", &input);switch (input){case 0:break;default:hannoi(input, 'A', 'B', 'C');break;}} while (input);return 0;
}
青蛙跳台阶
游戏规则

初阶版本

​ 青蛙一次可以跳一级台阶,也可以跳两级台阶。求该青蛙跳n级台阶共有多少种跳法?

进阶版本

​ 青蛙一次可以跳一级台阶,也可以跳两级台阶,……,也可以跳n级台阶,求该青蛙跳上n级台阶的跳法种数。

解题思路

我们反向思考,当青蛙跳到最高阶NNN阶时,他是怎么跳到第NNN阶的呢?

有两种情况,

  • 从第N−1N-1N−1阶,跳到第NNN阶,最后一次跳一阶。
  • 从第N−2N-2N−2阶,跳到第NNN阶,最后一次跳两阶。

图中用灰框框出的部分,是最后一次跳一阶的,其余的是最后一次跳两阶的。

很显然,除了这两种情况,别无他法。所以计算青蛙

跳到NNN阶的方法数 === 跳N−1N-1N−1阶的方法数 +++ 跳N−2N-2N−2 阶的方法数。

同样,图中用灰框框出的部分,也代表的是跳N−1N-1N−1阶的方法数,其余的是跳N−2N-2N−2 阶的方法数。

这其实就是斐波那契数列。

int fib(int n)
{if (n > 1)return fib(n - 1) + fib(n - 2);elsereturn 1;
}

C语言详解:函数递归专题相关推荐

  1. 你是真的“C”——详解函数递归+求解青蛙跳台阶问题

    详解函数递归运用+求解青蛙跳台阶问题

  2. c语言霍夫曼函数,使用C语言详解霍夫曼树数据结构

    1.基本概念 a.路径和路径长度 若在一棵树中存在着一个结点序列 k1,k2,--,kj, 使得 ki是ki+1 的双亲(1<=i 从 k1 到 kj 所经过的分支数称为这两点之间的路径长度,它 ...

  3. smali语言详解之一般/构造方法(函数)的声明与返回值关键字

    smali语言详解之一般/构造方法(函数)的声明与返回值关键字 一. smali语言的方法声明格式 .method与.end method成对出现,类似于java中的花括号 1.1.非静态的一般方法 ...

  4. Drools 规则语言详解(上)

    http://www.blogjava.net/guangnian0412/archive/2006/06/09/51574.html http://www.blogjava.net/guangnia ...

  5. 深入详解函数的柯里化

    深入详解函数的柯里化 一.补充知识点之函数的隐式转换 JavaScript作为一种弱类型语言,它的隐式转换是非常灵活有趣的.当我们没有深入了解隐式转换的时候可能会对一些运算的结果会感动困惑,比如4 + ...

  6. c++指针详解_c语言详解sizeof

    一.sizeof的概念 sizeof是C语言的一种单目操作符,如C语言的其他操作符++.--等. 它并不是函数. sizeof操作符以字节形式给出了其操作数的存储大小. 操作数可以是一个表达式或括在括 ...

  7. 如何用c语言编写stm32的程序吗,STM32入门C语言详解

    <STM32入门C语言详解>由会员分享,可在线阅读,更多相关<STM32入门C语言详解(6页珍藏版)>请在人人文库网上搜索. 1.最新 料推荐阅读 flash : 芯片内部存储 ...

  8. educoderpython答案计算机问题之递归第一关_Python练习题详解之递归(小白友好)...

    Python练习题详解之递归(小白友好) 你好!欢迎来到Python练习题详解系列.为了让小白(也就是我本人)更好的理解代码,打好编程基础,我决定仔细地解说一些练习题.欢迎阅读!奥利给! 首先,我们来 ...

  9. educoderpython答案计算机问题之递归_educoderpython答案计算机问题之递归第一关_Python练习题详解之递归(小白友好)......

    Python练习题详解之递归(小白友好) 你好!欢迎来到Python练习题详解系列.为了让小白(也就是我本人)更好的理解代码,打好编程基础,我决定仔细地解说一些练习题.欢迎阅读!奥利给! 首先,我们来 ...

  10. 克鲁斯卡尔算法c语言,Kruskal算法(一)之 C语言详解

    最小生成树 在含有n个顶点的连通图中选择n-1条边,构成一棵极小连通子图,并使该连通子图中n-1条边上权值之和达到最小,则称其为连通网的最小生成树. 例如,对于如上图G4所示的连通网可以有多棵权值总和 ...

最新文章

  1. 如何解决Python3写入CSV出现‘gbk‘ codec can‘t encode的错误
  2. Nature Methods:快速准确的微生物来源追溯工具FEAST
  3. R语言使用igraph包进行网络(network)可视化实战
  4. OIer同样是音乐家
  5. 编辑器统一 快捷键
  6. XGBoost数据训练小例子
  7. js vue 截取分割字符串数据
  8. Markdown记录
  9. 弘辽科技:拼多多直通车测款是怎样操作的
  10. 360网站域名拦截检测 非法网址检测系统原理
  11. 财报识别OCR,披露虚假财务报表
  12. 51单片机-TLC5615代码
  13. MySQL - CALL 语句
  14. 在线教育网站的一些瞎折腾……
  15. RVIZ 的菜单背景变成黑色
  16. 烤仔建工 | 来红浪漫和漂亮姐姐Yanelis K歌吧
  17. 【服务器数据恢复】服务器硬盘进水后服务器崩溃的数据恢复案例
  18. Qt编写的CAN通信调试工具源代码支持吉阳光电CAN盒和致远周立功USB转CAN卡,带多线程接收 可完成标准和扩展CAN帧YID发送和接收,带配置参数自动保存,定时发送,帧类型选择,文本和十六进制等
  19. 使用opencv检测视频人脸
  20. YouTube-8M视频数据集概览

热门文章

  1. 单片机毕业设计 基于stm32的自动泊车系统
  2. 求二进制数中有多少个1
  3. 用友U8视频教程全集(销售管理)-金速鹏-专题视频课程
  4. 视频教程-Layer 弹窗入门-jQuery
  5. 多媒体计算机设计原则,多媒体课件制作应当遵循的几个基本原则
  6. PLSQL解锁表操作
  7. P5224 Candies
  8. 2020-12-28 TCP协议
  9. 搜索软件Elastic上市:市值近50亿美元 是开源项目商业化范本
  10. WMS系统如何提高数据分析能力