[日常练习] 9. 基于函数递归与非递归的例题详解!

本文通过多个实例深入解析递归与非递归算法在求解斐波那契数列、幂运算、数字和、字符串逆序、阶乘及数字位数打印等问题中的应用,对比两种方法的优劣。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述:

1 . 递归和非递归分别实现求第n个斐波那契数    1、1、2、3、5、8、13、21、……
2 . 编写一个函数实现n^k,使用递归实现
3 . 写一个递归函数DigitSum(n),输入一个非负整数,返回组成它的数字之和,例如,调用DigitSum(1729),则应该返回 

     1+7+2+9,它的和是19
4 . 编写一个函数reverse_string(char * string)(递归实现)

     实现:将参数字符串中的字符反向排列

     要求:不能使用C函数库中的字符串操作函数
5 . 递归和非递归分别实现求n的阶乘
6 . 递归方式实现打印一个整数的每一位


1. 斐波那契数的规律是前两个数均是1,从第三个数开始,第三个数及以后的数值均为后两数的和。函数体中主要实现后两数相加赋给第三个数,并且循环这个函数体那么就能得到任意一个斐波那契数。而这个我们要实现循环体的自调用,循环体自己调用自己便是递归!源代码及结果如下:

非递归实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Fib(int n) {
	int a = 1;
	int b = 1;
	int c = 0;
	int i = 0;
	if (n <= 2) {                    
		return 1;
	}
	else {
		for (i = 0; i < n-2; i++) {
			c = a + b;
			a = b;
			b = c;
		}
		return c;
	}
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	a = Fib(a);
	printf("%d\n", a);
	system("pause");
	return 0;
}

非递归实现较为直观,下来我们来看看递归实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Fib(int n) {
	if (n <= 2) {                               //前两个数均是1 
		return 1;
	}
	else {
		return Fib(n - 1) + Fib(n - 2);     //递归主体
	}
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	a = Fib(a);
	printf("%d\n", a);
	system("pause");
	return 0;
}

检查结果是正确的!但是提出几点思考问题,可以试试计算第40、50(不考虑溢出时),它的效率是非常低的,在计算第40个斐波那契数时,我们将其拆分计算38、39,在计算38时将其拆分为37、36,在计算39时拆分为38、37,这样便有:

40

39          38

38           37           37           36

37           36           36           35           36           35           35           34

.................................

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int count = 0;                                      //全局变量
int Fib(int n) 
{
	if (n == 3) {                               //计算3被重复计算的次数
		count++;
	}
	if (n <= 2) {                               //前两个数均是1 
		return 1;
	}
	else {
		return Fib(n - 1) + Fib(n - 2);     //递归主体
	}
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	a = Fib(a);
	printf("%d\n", a);
	printf("重复计算:%d\n", count);
	system("pause");
	return 0;
}

现在便开始重复了,在计算较大数时,小的数字会计算的越来越多,在计算40是,可以添加计数count打印3被计算的次数竟有3000多万次!!!这个效率是非常低的,相较于非递归时,这个相当的不适用!

递归写出来的表现形式很简单,但是想法思路比较难!并且在此可以看出,非递归更胜一筹,那么何时采用递归、何时采用非递归便是我们需要考虑的!


2. 有了前面对斐波那契数第递归实现热身战,阶乘的递归实现很简单,直接给出源代码及结果如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int M(int i, int k) {
	if (k == 0) {
		return 1;
	}
	else {
		return i * M(i, k-1);
	}
	
}

int main()
{
	int n = 0;
	int k = 0;
	scanf("%d %d", &n, &k);
	printf("%d\n", M(n, k));
	system("pause");
	return 0;
}

再次领略“递归”的神奇之处,它的语句很简单但很有威力,使函数重复进行自调用。在这,能完美的解决这个问题!


3.  在这道题中要有一种“拆分”的思想,需要将非负两位数以上整数的各个位数的数字进行相加,那么重点自然就转到了怎么求得各个数位上的数字,再相加即可。思考后发现,从低位到高位依次取时:先模10,数字不变,再除10,保留结果再进行模,结果不变,再除10,保留结果再模...当它变成1位数的时候结束!

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int P(int a) {
	int sum = 0;
	if (a / 10 == 0) {
		return a;
	}
	else {
		return sum = a % 10 + P(a / 10);   //将1823分解为 182 3 
	}                                          //将182分解为  18  2
}                                                  //将18分解....

int main()
{
	int i = 0;
	scanf("%d", &i);
	printf("%d\n", P(i));
	system("pause");
	return 0;
}

结果正确,注意理解递归体内的思想,以及多位数的拆分思想!


4. 逆序C风格字符串,练习的已经很多遍了。题目要求参数的指针,我们在此将其传为数组,由第一题知道,这两者完全等价!在函数体内没有办法得到数组元素个数,而C风格字符串可以采用strlen函数完美解决这个问题。大家都知道C风格字符串的特殊之处在于后面多了一个'\0',那么strlen函数会不会也计算它呢?仔细甄别定义!

非递归实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

void reverse_string(char arr[]) {
	char *left = arr;
	char *right = arr + strlen(arr) - 1;
	while (left < right) {
		char tmp = *left;
		*left = *right;
		*right = tmp;
		left++;
		right--;
	}
}

int main()
{
	char arr[] = "abcdef";
	reverse_string(arr);
	printf("%s\n", arr);
	system("pause");
	return 0;
}

递归实现:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>

void reverse_string(char arr[]) {
	int len = strlen(arr);
	char tmp = *arr;
	*arr = *(arr + len - 1);
	*(arr + len - 1) = '\0';
	if (strlen(arr + 1) > 1) {         //理解
		reverse_string(arr + 1);
	}
	*(arr + len - 1) = tmp;
}

int main()
{
	char arr[] = "abcdef";
	reverse_string(arr);
	printf("%s\n", arr);
	system("pause");
	return 0;
}


5. 有了前面对众多例题的详解,阶乘的递归实现很简单,直接给出源代码及结果如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int factorial(int i) {
	if (i <= 1) {
		return 1;
	}
	else {
		return i * factorial(i - 1);
	}
	
}

int main()
{
	int a = 0;
	scanf("%d", &a);
	printf("%d\n", factorial(a));
	system("pause");
	return 0;
}


6. 在第3题的时候,我们得到了各个位数相加的和,即得到了各个位数的具体数字。经过稍加改动,可以将各个数位的数字分别输出,但是,却是倒序输出。针对这个情况,答案近在咫尺,就是“模”、“除”的顺序关系只需颠倒一下就圆满完成任务了,源代码及结果如下:

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Print(int a) {
	int i = 0;
	if (a / 10 == 0) {
		return a;
	}
	else {
		i = Print(a / 10);
		printf("%d ", i);
		return (a % 10);
	}
}

int main()
{
	int i = 0;
	scanf("%d", &i);
	printf("%d\n", Print(i));
	system("pause");
	return 0;
}

结果正确,在此抛出一个问题:如何正确理解递归函数体具体数值变化关系?

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ypuyu

如果帮助到你,可以请作者喝水~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值