【C语言初阶(11)】递归练习题

news/2024/7/7 19:14:31

文章目录

  • 1. 打印一个整型的每一位
  • 2. 求字符串长度
  • 3. 求 n 的阶乘
  • 4. 求第 n 个斐波那契数
    • 4.1 递归算法
    • 4.2 非递归算法

1. 打印一个整型的每一位

题目内容

  • 接受一个整型值(无符号),按照顺序打印它的每一位。
    • 例如:输入 1234,输出 1 2 3 4。

如何得到 1234 的每一位?

  1. 如果想得到 1234 的最后一位的话,只需要 1234 %10 就可以得到 4,得到 4 之后将之打印出来;
  2. 既然已经得到了 4,那么就不在需要它了,此时用 1234 / 10 = 123 就能去掉最后一位数;
  3. 重复上面两个步骤,直到 1234 只剩个位数的时候停止循环即可。

在这里插入图片描述

  • 此时已经了解了如何获取一个多位数上的每位数字,但是这个结果显然不是我们想要的正序打印,此时就该了解如何使用递归来求解了。

递归实现的解题思路

  • 通过前面我们已经知道 1234 最好拿到的是最后一位的 4,然而我们想要正序打印就必须先拿到第一位的 1,然后将之打印出来。
  • 也就是说,我们应该通过递归不停地 n / 10,直到 n 只剩第一位的 1 时结束递进开始回归。回归的过程中就可以很好的将 1234 的每一位打印出来了。

代码实现

#include <stdio.h>
void print(unsigned int n)
{
	if (n > 9)// n>9 说明 n 还有的拆
	{
		print(n / 10);
	}
	printf("%u ", n % 10);//递推结束开始回归时这条语句才能被执行
}
int main()
{
	unsigned int num = 1234;
	print(num);
	return 0;
}

在这里插入图片描述

代码分析

看到递归了吗
在这里插入图片描述

在这里插入图片描述

2. 求字符串长度

题目内容

  • 编写函数不允许创建临时变量,求字符串的长度。
  • 一般而言,像这种求字符串长度的题,创建临时变量来做的话都蛮简单的。

在这里插入图片描述

  • 但是不允许创建临时变量就显得很变态了,此时只能使用递归来写了。

解题思路

  • 数组在传参的时候传过去的是数组首元素的地址,如果这个地址指向的第一个字符不是 \0 那串的长度就至少为 1,就可以进行递归;
  • 每次递归让地址 +1 指向下一个字符,并且数量 +1 ,直到遇到 \0 递归结束,开始返回。
  • 在回归过程中将遇到的所有 1 全部加起来就是串的长度。

代码实现

#include <stdio.h>
int my_strlen(char* str)
{
	//如果第一个字符不等于 \0,那它的长度至少是1
	if (*str != '\0')
	{
		return 1 + my_strlen(str+1);
	}
	else
	{
		return 0;
	}
}
int main()
{
	char arr[] = "abc";
	printf("%d\n", my_strlen(arr));
	return 0;
}

在这里插入图片描述

代码分析

my_strlen(“abc”)
1 + my_strlen(“bc”)
1+1+my_strlen(“c”)
1+1+1+my_strlen(“”)

图解1
在这里插入图片描述

3. 求 n 的阶乘

阶乘公式

在这里插入图片描述

  • 有了公式的话就很好解决了,递归套用公式就行了。

代码实现

#include <stdio.h>
int fac(int n)
{
	if (n > 1)
	{
		return n * fac(n - 1);
	}
	else
	{
		return 1;// 0 和 1 的阶乘都是 1
	}
}
int main()
{
	int n;
	scanf("%d", &n);//以 n =  5 做例子
	printf("%d\n", fac(n));
	return 0;
}

在这里插入图片描述

代码分析

在这里插入图片描述
在这里插入图片描述

4. 求第 n 个斐波那契数

斐波那契数

  • 斐波那契数列指的是这样一个数列:
    • 1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584,4181,6765,10946,17711……
  • 它们的规律是:这个数列从第 3 项开始,每一项都等于前两项之和。

4.1 递归算法

斐波那契数公式

  • 通过求阶乘那节可以发现,在有了函数公式的情况下,递归写起来基本就是有手就行了;
  • 斐波那契数当然也有自己的公式:

在这里插入图片描述

代码实现

#include <stdio.h>
int Fib(int n)
{
	if (n > 2) //从第三位才开始求斐波那契数
	{
		return Fib(n - 1) + Fib(n - 2);
		//公式:fib(n) = fib(n-1) + fib(n-2)
	}
	else
	{
		return 1; // 前两个斐波那契数都是 1
	}
}
int main()
{
	int n;
	scanf("%d", &n);
	printf("%d\n", Fib(n));
	return 0;
}

在这里插入图片描述

  • 在使用 fib 这个函数的时候如果我们要计算第 50 个斐波那契数字的时候特别耗费时间。
    • 博主的电脑在求第 50 个斐波那契数的时候用了有足足两分钟。

不推荐使用递归求斐波那契数

  • 在使用递归求解斐波那契数的时候,会出现大量重复性的计算,导致程序执行效率很底。
  • 如下图:想求第 6 个斐波那契数就得先求出第 5 个以及第 4 个斐波那契数;
  • 想求出第 5 个斐波那契数就又要先求 第 4 个以及第 3 个斐波那契数,这才第二步就开始出现重复运算了。

在这里插入图片描述

  • 来看看在求第 40 个斐波那契数的过程中,重复求了第 3 个斐波那契数多少次:
#include <stdio.h>
int count_fib_3 = 0;
int Fib(int n)
{
	//记录在运算某个数的过程中求了多少次第 3 个斐波那契数
	if(3==n)
	{	
		count_fib_3++;
	}
	if (n > 2)
	{
		return Fib(n - 1) + Fib(n - 2);
	}
	else
	{
		return 1;
	}
}
int main()
{
	int n;
	scanf("%d", &n);
	printf("%d\n", Fib(n));
	printf("%d\n", count_fib_3);
	return 0;
}

可以看到,光是第 3 个斐波那契数就重复求了将近四千万次,那是相当的夸张。
在这里插入图片描述

4.2 非递归算法

  • 为了解决用递归求斐波那契数出现的大量的重复计算现象,使用迭代求斐波那契数无疑是一个更好的选择。

迭代定义

  • 迭代就是重复的意思;
  • 循环是迭代,迭代不一定是循环。

解题思路

  • 使用递归我们是从后往前算的,那么使用迭代我们就从前往后算。
  • 使用三个变量来存储 第 1、2 个数以及前两数之和。
  • 通过不断交换三个变量的值,从而在一条斐波那契数列上进行移动。

解题步骤

  1. 先将变量 a 和 b 的值都赋成 1,然后执行 a + b = c ;
    • 从第 3 个数开始才去求斐波那契数,因为前两个斐波那契数都是 1。
  2. 算好之后将 b 的值赋给 a,将 c 的值赋给 b,然后执行 a + b = c,产生新的变量 a b c,然后将 n 的值减 1;
  3. 重复上面两个步骤,直到 n 的值等于 3 结束循环。

代码实现

#include <stdio.h>
int Fib(int n)
{
	int a = 1, b = 1, c = 1;
	while(n >= 3)
	{
		c = a + b;
		a = b;
		b = c;
		n--;
	}
	return c;
}
int main()
{
	int n;
	scanf("%d", &n);
	printf("%d\n", Fib(n));
	return 0;
}
  • 现在算第 50 个斐波那契数虽然结果是错的,但是效率提升上来了。

在这里插入图片描述

  • 使用了迭代的方法计算斐波那契数之后,需要重复计算的步骤就彻底被干掉了。

在这里插入图片描述


http://lihuaxi.xjx100.cn/news/1313047.html

相关文章

springboot2.4实现事件监听

springboot2.4实现事件监听 物料准备&#xff1a; 1.一个事件类 2.一个监听方法 3.发布事件 自定义一个事件类MyEvent.java package com.example.demo.evt;import lombok.Data;/*** 1.创建事件类** 创建1个事件类&#xff0c;用于封装你想传递的数据。* 这个事件类可以是…

【java】hutool发送http请求,配置ssl忽略

1.发送请求 import cn.hutool.http.HttpRequest; /*** cf*/ public class TqOdpServiceClient {private static String url"url";;public static String execute(String http,String params,String auth) {String result2 HttpRequest.post(httpurl).header("A…

深度学习-目标检测之边界框bbox坐标转换公式汇总

深度学习 文章目录 深度学习 尝试着写了一个可以转换任何维度的任意格式的bbox函数。 本程序目的是&#xff1a; 可以转换以下三种格式的输入数据 list,numpy,tensor&#xff0c;维度可以从0维到2维&#xff0c; 也就是shape为&#xff1a;(4,) (3, 4) torch.Size([4]) torch.…

【rtklibexployer】RTKLIB Static-start feature 静态启动模式

原文&#xff1a;RTKLIB: Static-start feature 静态启动模式是我不久前添加到 demo4 代码中的&#xff0c;但一直没来得及解释&#xff0c;所以我现在要做。 正如我以前提到的&#xff0c;我总是喜欢在第一次打开接收器后让接收机静止不动&#xff0c;以便在允许它移动之前获…

svn和git的使用杂谈

笔者毕业的4年一直都是使用svn&#xff0c;直到前面几个月换了工作&#xff0c;新单位用的git&#xff0c;愣是用了1-2个月的时间才缓过来&#xff1b;下面是笔者关于这两款代码管理工具使用的一点经验。 svn svn:使用简单&#xff0c;入门容易&#xff0c;就只有本地代码和远…

jenkins部署springboot项目

jenkins部署springboot项目 1、创建一个项目 上传到gitee 1、创建项目 2、上传到git 2、jenkins创建一个pipeline项目 Pipeline简介 1&#xff09;概念 Pipeline&#xff0c;简单来说&#xff0c;一套运行在 Jenkins 上的工作流框架&#xff0c;将原来独立运行于单个或者…

Markdown(6):段落缩进

Markdown 段落缩进 \qquad Markdown 是一种轻量级标记语言&#xff0c;创始人为约翰格鲁伯&#xff08;John Gruber&#xff09;。 它允许人们使用易读易写的纯文本格式编写文档&#xff0c;然后转换成有效的 XHTML&#xff08;或者HTML&#xff09;文档。这种语言吸收了很多在…

SSL证书中的对称加密是什么?有哪些算法

SSL证书中的对称加密是一种加密类型&#xff0c;它使用相同的密钥来加密和解密数据。发送者和接收者都具有相同的密钥副本&#xff0c;它们是秘密的&#xff0c;不会与任何人共享。这与非对称加密不同&#xff0c;后者使用两个密钥-一个公共密钥&#xff08;任何人都可以访问&a…