IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 递归是什么 -> 正文阅读

[C++知识库]递归是什么

递归的实质

我们知道:函数可以调用其他函数实现部分功能,那么,函数可不可以调用自己呢?
答案:当然!
在这里插入图片描述

故事

从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说:从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说:从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说:……

怎么用C语言打印出来呢?
简单:不就是循环嘛!

#include<stdio.h>
int main(){
	int i=1;
	for(i=1;i<=20;i++){
		printf("从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说\n");}
	return 0;
}

重复打印的事,我们可以交给函数执行:改!
也就是之后无论哪个和尚说这个故事都可以调用这个函数,节省代码量和时间!在这里插入图片描述

#include<stdio.h>
void digui();
void digui(){
	int i=1;
	for(i=1;i<=20;i++){
		printf("从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说\n");}
}
int main(){
	digui();
	return 0;
}

欸,可是函数里面这句话也是一直重复的呀。。。
在这里插入图片描述

我自己调用自己一直重复打印这句话不就好了吗!

#include<stdio.h>
void digui();
void digui(){
	static int i=1;
	//静态局部变量,每次都保留上一次的值,但是作用域没变,即递归调用自己时不会初始化为1
	printf("从前有座山,山上有座庙,庙里有两个和尚,老和尚对小和尚说\n");
	i++;
	if(i<=20){
		digui();}	
}
int main(){
	digui();
	return 0;
}


静态局部变量(static)
static 用于描述具有文件作用域的变量或函数时,表示将其链接属性从external 修改为 internal,它的作用范围就变成了仅当前源文件可以访问。
但如果将 static 用于描述局部变量,那么效果又会不一样了。默认情况下,局部变量是 auto 的,具有自动存储期的变量。如果使用 static 来声明局部变量,那么就可以将局部变量指定为静态局部变量。static 使得局部变量具有静态存储期,所以它的生存期与全局变量一样,直到程序结束才释放。

递归从原理上来说就是函数调用自身这么一个行为。

来一个错误示范:

#include<stdio.h>
void recursion(void);
void recursion(void){
	printf("hi\n");
	recursion();//自己调用自己,无限月读!
}

int main()
{
	recursion();
	return 0;
}

别试!会炸!

于是我们悟道了一个道理:

编写递归程序需要注意的地方

递归程序需要正确设置结束条件,否则递归程序会一直走下去,直到崩溃。

#include<stdio.h>
void recursion(void);
void recursion(void){
    static int count=10;
	printf("hi\n");
    if(--count)
    {
       	recursion();
    }
}

int main()
{
	recursion();
	return 0;
}

栗子:递归求阶乘

n ! = 1 ? 2 ? 3 ? . . . ? n n!=1*2*3*...*n n!=1?2?3?...?n

首先使用循环
使用累乘的思想循环,一次成一个项:

#include<stdio.h>
long fact(int num);
long fact(int num)
{
	long result;
	for(result=1;num>1;num--)
	{
		result*=num;
	}
	return result;
}
int main()
{
	int num;
	printf("请输入一个正整数:");
	scanf("%d",&num);
	fact(num);
	printf("数%d的阶乘为:%ld",num,fact(num));
	return 0;
}

修改为递归

n ! = ( n ? 1 ) ! ? n n!=(n-1)!*n n!=(n?1)!?n
( n ? 1 ) ! = ( n ? 2 ) ! ? ( n ? 1 ) (n-1)!=(n-2)!*(n-1) (n?1)!=(n?2)!?(n?1)
… …
2!=1! *2

所以若fact()为求阶乘函数,即 n ! = f a c t ( n ) n!=fact(n) n!=fact(n)
则有

n ! = f a c t ( n ) = f a c t ( n ? 1 ) ? n = f a c t ( n ? 2 ) ? ( n ? 1 ) ? n = . . . = f a c t ( 1 ) ? 2 ? . . . ? n n!=fact(n)=fact(n-1)*n=fact(n-2)*(n-1)*n=...=fact(1)*2*...*n n!=fact(n)=fact(n?1)?n=fact(n?2)?(n?1)?n=...=fact(1)?2?...?n

即在fact(n)函数中调用自己计算fact(n-1),而在这继续调用fact(n-2)…一直至fact(1),再逐层将结果返回

在这里插入图片描述

#include<stdio.h>
long fact(int num);
long fact(int num)
{
	long result;
	if(num>0)//结束条件
	{
		result=num*fact(num-1);//自己调用自己
	}
	else
	{
		result=1;
	}
	return result;
}
int main()
{
	int num;
	printf("请输入一个正整数:");
	scanf("%d",&num);
	fact(num);
	printf("数%d的阶乘为:%ld",num,fact(num));
	return 0;
}

例子:递归求解斐波那契数列

数列长这样子:

 1    1    2   3   5   8   13   21    34

规律:
a n = a n ? 1 + a n ? 2 a_n=a_{n-1}+a_{n-2} an?=an?1?+an?2?
若fun(n)函数为求解 a n a_n an?项值,则有
f u n ( n ) = f u n ( n ? 1 ) + f u n ( n ? 2 ) fun(n)=fun(n-1)+fun(n-2) fun(n)=fun(n?1)+fun(n?2)
即需要调用两次自己!

# include <stdio.h>
int fun(int n); 
int fun(int n){
	int num;
	if(n==1||n==2)
	{
		num=1;
	}
	else
		num=fun(n-1)+fun(n-2);//调用自己
	return num;
}

int main (void)
{  
    int n;
    printf("Enter n:");
    scanf("%d",&n);
    
	printf("a_n=%d",fun(n));
	return 0;
}

对比一下用循环的思想做:

# include <stdio.h>
int fun(int n); 
int fun(int n){
	int num1,num2,num;
	int i;
	num1=num2=1;//纪录前两项为1 
	if(n==1||n==2)
	{
		num=1;//纪录前两项为1 
	}
	else
		for(i=3;i<=n;i++){
			num=num1+num2;//否则为前两项的和 
			//更新前两项 
			num1=num2;
			num2=num;
		}	
	return num;
}

int main (void)
{  
    int n;
    printf("Enter n:");
    scanf("%d",&n);
    
	printf("a_n=%d",fun(n));
	return 0;
}

递归的优势和劣势

优势:递归的思考角度跟通常的迭代(你可以理解为 for 循环之类的)迥然不同,所以有时候使用迭代思维解决不了的问题,使用递归思维则一下子迎刃而解。

劣势:递归的执行效率通常比迭代低很多,所以递归程序要更消耗时间;由于递归函数是不断调用函数本身,在最底层的函数开始返回之前,程序都是一致在消耗栈空间的,所以递归程序要“吃”更多的内存空间;递归的结束条件设置非常重要,因为一旦设置错误,就容易导致程序万劫不复(崩溃)。

汉诺塔

在线小游戏:https://www.hannuota.cn/

假设有64个盘子:对于汉诺塔的玩法,可以简单分解为三个步骤:

  • 将前 63 个盘子从 X 移动到 Y上,确保大盘在小盘下。
  • 将最底下的第 64 个盘子从 X 移动到 Z 上。
  • 将 Y 上的 63 个盘子移动到 Z 上。

在游戏中,由于每次只能移动一个圆盘,所以在移动的过程中显然要借助另外一根针才可以实施。也就是说,步骤 1 将 1~63 个盘子移到 Y 上,需要借助 Z;步骤 3 将 Y 针上的 63 个盘子移到 Z 针上,需要借助 X。

所以我们把新的思路聚集为以下两个问题:

  • 问题一:如何将 X 上的 63 个盘子借助 Z 移到 Y 上?
  • 问题二:如何将 Y 上的 63 个盘子借助 X 移到 Z 上?

解决这两个问题的方法跟解决“如何将 X 上的 64 个盘子借助 Y 移动到 Z 上?”这个问题是一样的,都是可以拆解成 1、2、3 三个步骤来实现。

问题一(“如何将 X 上的 63 个盘子借助 Z 移到 Y 上?”)拆解为:

  • 将前 62 个盘子从 X 移动到Z上,确保大盘在小盘下。
  • 将最底下的第 63 个盘子移动到 Y 上。
  • 将 Z 上的 62 个盘子移动到 Y 上。

问题二(“如何将 Y 上的 63 个盘子借助 X 移到 Z 上?”)拆解为:

  • 将前 62 个盘子从 Y 移动到 X 上,确保大盘在小盘下。
  • 将最底下的第 63 个盘子移动到 Z 上。
  • 将 X 上的 62 个盘子移动到 Y 上。

没错,汉诺塔的拆解过程刚好满足递归算法的定义,因此,对于如此难题,使用递归来解决,问题就变得相当简单了!

#include <stdio.h>

void hanoi(int n, char x, char y, char z);

void hanoi(int n, char x, char y, char z)
{
        if (n == 1)
        {
                printf("%c --> %c\n", x, z); // 剩下底部的那个圆盘
        }
        else
        {
                hanoi(n-1, x, z, y); // 将n-1个圆盘从x移动到y
                printf("%c --> %c\n", x, z);
                hanoi(n-1, y, x, z); // 将n-1个圆盘从y移动到z
        }
}

int main(void)
{
        int n;

        printf("请输入汉诺塔的层数:");
        scanf("%d", &n);

        hanoi(n, 'X', 'Y', 'Z');

        return 0;
}

参考资料

鱼C工作室、论坛

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-10-30 12:23:34  更:2021-10-30 12:24:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 4:52:48-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码