【C++】递归

2023-10-31

1.什么是递归?

程序调用自身的编程技巧称为递归。递归做为一种算法在程序设计语言中广泛应用。 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法,它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算,大大地减少了程序的代码量。递归的能力在于用有限的语句来定义对象的无限集合。一般来说,递归需要有边界条件、递归前进段和递归返回段。当边界条件不满足时,递归前进;当边界条件满足时,递归返回。

递归程序设计是C++语言程序设计中的一种重要的方法,它使许多复杂的问题变得容易了。只是你需要花费一些时间去深刻理解。

2. 递归的作用

递归算法一般用于解决三类问题:
(1)数据的定义是按递归定义的。(Fibonacci函数)
(2)问题解法按递归算法实现。
这类问题虽则本身没有明显的递归结构,但用递归求解比迭代求解更简单,如Hanoi问题。
(3)数据的结构形式是按递归定义的。
如二叉树、广义表等,由于结构本身固有的递归特性,则它们的操作可递归地描述。

3.递归的一般形式:

void rec(形参列表)
{
	if(test) return;  //边界条件
    //!!!注意!!!  递归一定要有边界条件!!!否则就会死循环!!!
    rec(实参列表)     //递归调用
    语句序列2         //递归返回段(回溯)
}

!!!注意!!! 递归一定要有边界条件!!!否则就会死循环!!!

4.举个例子

阶乘

阶乘是递归的典型问题之一。
阶乘函数可递归地定义为:
在这里插入图片描述

下面是一个用递归求阶乘的代码

#include<bits/stdc++.h>
using namespace std;
int fac(int n)
{
	if(n==0) return 1;//如果参数是0或者1返回1
	else return n*fac(n-1);//否则返回n和下次递归的积
}
int main()
{
	int n;
	cin>>n;
	cout<<fac(n);//函数调用
	return 0;
}

5. 递归趣文

此文章来自 http://www.matrix67.com/blog/archives/30,欢迎大家去大神的博客膜拜。

公认的递归(Recursion)的标准定义是非常难理解的:若一个对象部分地包含它自己,或用它自己给自己定义,则称这个对象是递归的;若一个过程直接地或间接地调用自己,则称这个过程是递归的过程。
递归一词很少有过专业的定义,因此本文不在于去解释上一段文字的意义。虽然概念抽象,但递归其本身是不难理解的。通过本文的介绍,读者不一定能深入了解递归,只要能通过具体的例子模模糊糊地知道一些递归的思想和用途就可以了。
究竟什么是递归呢?其实,递归就是大鱼吃小鱼,就是一条蛇咬住自己的尾巴。递归是指一样东西自己包含了自己。对于这一点,拿“谢尔品斯基地毯”(Sierpinski Gasket)来说明是最恰当不过的了。
曲线在几何学中的概念很好理解,就是只有长度而没有宽度的线条。数学中有各种各样的曲线,如圆、直线、抛物线、双曲线、正弦曲线等。它们都可以用一定的方法画出来。例如,圆可以用圆规画出来,正弦曲线也可以用机器边在纸带上往复记号边拉纸带的方法画出来。事实上却没那么麻烦,画曲线有一个最常用的“万能方法”——似乎所有的曲线都可以用“描点法”画出,因为曲线没有宽度嘛,一个一个的点连起来,随便多奇怪的曲线都应该能画出。但随着数学的发展,这一点遭到了置疑。波兰数学家谢尔品斯基就想出了一个的确是一种曲线但永远无法画出的图形。他构造这种曲线的方法就运用了递归。
随便找了一个正方形,把它分成3×3规格的相等的9个小正方形,然后把正中间的那一个挖掉。现在就只剩周围的八个小正方形了。接着重复这个过程,把8个小正方形的每一个都分成更小的9份,并挖掉它中间的那个。现在得到的就有8×8=64个正方形了。把这64个正方形继续这样划分,并且无限制的继续下去。这就是递归的思想,自己包含了自己,而后面的自己又包含了规模更小的自己。这样递归下去是没完的,因此最终得到的会是没有宽度的线条。这符合曲线的定义,但显然它是没办法画出来的。
在现实生活中,递归的现象也是可以见到的。如果一台电视机的屏幕正显示着摄象机拍到的东西,那么把摄象机正对着这台电视机拍摄就会形成一个简单的递归。电视机显示着摄象机拍到的内容,而摄象机又对着电视机,这也就是说,摄象机将会拍摄到自己所拍到的东西。于是,递归形成了——在电视机上会显示出一层一层电视机的轮廓,即电视机里有电视机,层层循环下去永无止境。类似的例子也有一些,比如那个永远也讲不完的古老的故事,和Linda的第二张专辑的封面。
递归通常是可以无限循环下去的。因此有这样一个笑话。作为一个狂热的电脑游戏迷,如果有一天你从一个完全陌生的地方醒来,你如何判断这是虚拟空间还是在现实中?答案是,找两面镜子来,互相对着放。如果出现周围的物体运动变慢等不正常的情况,说明你是在虚拟空间中。大自然是神奇的,它能处理两面镜子相对放置时镜子里应该显示的内容;但电脑就模拟不出来,如果电脑真遇到这种情况,指定会把CPU累死。
但是,一旦给一个递归过程加上一个限制条件,让它递归到某一步时就停下来不要继续循环的话,递归将会派上大用场。
我举一个最简单的例子。偶数就是能被2整除的数。我也可以用递归的方法定义偶数:一个偶数加上2还是偶数。这句话似乎足以说明了全部的数字,其实不然。因为如果没有任何限制,那么这个递归过程将是永无止境的,最终不会得到任何具体的答案。我们可以加上一个条件“0是偶数”。这样,情况就变了。比如,我们要看6是否为偶数,根据“一个偶数加上2还是偶数”,我们只需要看4是不是偶数。如果4是偶数,那么4+2也是偶数。而看4是否为偶数,又要看2是否为偶数,要看2是否为偶数,又要看0是否为偶数。本来这个递归应该是像这样无限地做下去的,但我们有了一个限制条件:我们已经知道了0是偶数。于是,2就是偶数了,4和6都是偶数了。同样的,我们就可以判断一切数字的奇偶性了。这就是利用递归来进行数学上的定义。
这种定义方式有什么好处呢?一个简单的例子——
很多人不明白为什么0的阶乘要规定成1,其实这用阶乘的递归定义一解释就清楚了。
阶乘可以这样递归地定义:
1)n的阶乘等于n-1的阶乘乘以n;
2)1的阶乘是1;
这样,所有自然数的阶乘就可以通过上面的两句话表示了。2的阶乘就是1×2;3的阶乘就是2的阶乘乘3,即1×2×3……不仅如此,我们还可以知道0的阶乘是多少:1的阶乘应该等于0的阶乘乘以1,显然0的阶乘必须是1才行。类似的,我们还能知道,负整数的阶乘没有意义。
接下来,我将用两个经典的用递归的思想解决问题的例子来结束这篇文章。
法国数学家艾得渥·卢卡斯(Edouard Lucas)于1883年在一份杂志上介绍了一个引人入胜的数学谜题——汉诺塔(Tower of Hanoi),并称这与古印度的一个传说有关。显然传说的具体内容已经不在本文论述的范围内了,但我想简单的介绍一下。
相传印度有座大寺庙,它曾被认为是宇宙的中心。神庙中放置了一块上面插有三根长木钉的木板,在其中的一根木钉上,由上至下放了64片直径由小至大的圆环形金属片。古印度教的天神指示他的僧侣们将64片金属片全部移至另一根木钉上。移动规则只有两个:
1.在每次的移动中,只能移动一片金属片;
2.过程中任意时刻必须保证金属片小的在上,大的在下。
直到有那么一天,僧侣们能将64片金属片按规则从指定的木钉上全部移至另一根木钉上,那么,世界末日即随之来到,世间的一切终将被毁灭,万物都将至极乐世界。
这个传说经常被认为是卢卡斯教授为推广这个数学谜题而捏造的,但不管怎么说,卢卡斯是成功了。这玩意儿变成了家喻户晓的益智游戏之一,后来又成为了学习递归的必修课程。
对汉诺塔问题的研究焦点集中在如何以最少的步骤完成全部金属片的转移这一问题上。解决这个问题的方法运用了递归的思想。
我们可以这样想。64片金属片太多了,我们似乎能简化一下。假如我们已经知道了如何移动63片,我们就可以把这63片看成一个整体。那么这64片的移动过程就出来了:第一步,移动前63片到另一根木钉上;第二步,移动
第64片到第三根木钉上;第三步,把那63片移回第64片上面。看到了吗?问题已经解决了,因为这形成了递归。我们可以继续对移动63片的方法进行研究:把前62片移开,移动第63片,移回前62片。继续研究62片金属的移动方法……这样下去,一直推到如何移动2片金属。而移动2片金属的方法是非常简单的,已经不需要继续讨论了,于是,全部问题到此解决。
发现递归思想的实质了吗?这让我想起了一个笑话。笑话的主人公是一个反应迟钝,只具有数学思维的数学家;为了使这个笑话更形象,我们就把这个人暂且定为童明国(注:我们数学老师的名字)。
童明国去做消防队员。队长问:“如果你这里起火了,你怎么办?”童明国答:“用消火栓。”
“那么如果这里没有起火呢?”
“很简单,先把这里点燃,然后这个问题就转化为了一个我已经解决的问题了。”
我要举的下一个例子与这个有异曲同工之处。
小学奥赛接触过一个叫作“报30”的游戏,就是从1开始,两人轮流报数,每个人都只能报接下来的一个数或两个数。比如,第一个人可以报1,也可以报1、 2;如果第一个人报1、2,第二个人就可以报3和3、4;然后第一个人又报;这样报下去,最先报到30的人获胜。
这个游戏非常没意思,因为它有必胜策略。
最先报到30的人获胜,很显然,先报到27的人一定可以胜;那么,先报到24的人就一定能胜了……递归下去,21,18,最终得到的结论就是,先报到3的人一定必胜。也就是说,后报者必胜。不管先报者报多少,后报者始终报到3的倍数,这样定能获胜。
这个游戏有很多变种,但换汤不换药,万变不离其宗。比如,把规则改成“最先报到30的人就输”。这样,先报到29的人就赢了,然后同样递归,26,23,20……
前几天在网上看到了这个游戏的一个较难的变种。
有10枚硬币,每人轮流取硬币,可以拿一枚、两枚或四枚;取到最后一枚硬币者胜。
这样还有必胜策略吗?答案是肯定的,而且同样可以运用递归的思想来解决。
如果硬币的总数只有一枚,则先取者赢;
如果硬币的总数是两枚,则先取者赢;
如果硬币的总数是三枚,则先取者输;
如果硬币的总数是四枚,则先取者赢;
如果硬币的总数是五枚,则先取者赢(取两枚,对方面临三枚的情形,必输);
如果硬币的总数是六枚,则先取者输(不管取多少,对方面临的情形都是必胜的情形);
如果硬币的总数是七枚,则先取者赢(取一枚,对方面临六枚的情形,必输);
如果硬币的总数是八枚,则先取者赢(取两枚,对方面临六枚的情形,必输);
如果硬币的总数是九枚,则先取者输(不管取多少,对方面临的情形都是必胜的情形);
如果硬币的总数是十枚,则先取者赢(取一枚,对方面临九枚的情形,必输)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【C++】递归 的相关文章

随机推荐

  • STM32CubeMX时钟树(72MHZ主频配置)

    目录 一些基础概念 时钟树配置图 第一步 第二步 这里我只是配置常用的72MHZ主频 很多时候新手都在时钟树这里被劝退了 其实不知道没关系 我用STM32这么久了 也只知道大概 我们绝大多数时候不需要配置这个时钟 记住72MHZ主频配置即可
  • Bayer模型的颜色插值算法

    图像采集的功能一般用CCD和CMOS传感器来实现 但是这两种图像传感器在一个像素上只能采集 RGB颜色的一个分量 为了获得最佳的图像效果 需要3个图像传感器分别采集不同的颜色分量 但考虑 到产品的成本及设计复杂度 通常的数字成像设备用一个传
  • ​JVM第五讲 JVM内存结构

    JDK体系结构 Java语言的跨平台特性 JVM整体结构及内存模型 在minor gc过程中对象挪动后 引用如何修改 对象在堆内部挪动的过程其实是复制 原有区域对象还在 一般不直接清理 JVM内部清理过程只是将对象分配指针移动到区域的头位置
  • robot framework 使用三:他们主动浏览器的兼容性

    robot framework 浏览器兼容性测试 上图中黄色圈的地方默认什么都不写 是firefox浏览器 写上ie就是ie浏览器了 firefox最新版本号即可 ie须要设置 1 IE选项设置的安全页中 4个区域的启用保护模式的勾选都勾上
  • ARTS挑战打卡第十六周

    Algorithm 一周至少一道算法题 Review 阅读并点评至少一篇英文技术文章 Tip 学习至少一个技术技巧 总结和归纳在日常工作中所遇到的知识点 Share 分享一篇有观点和思考的技术文章 01 Algorthm https lee
  • 用java写个管理系统

    如果要用 Java 写一个管理系统 可以这样做 首先 确定管理系统的功能 然后设计系统的数据结构和架构 这可以通过绘制 UML 类图或流程图来完成 安装 Java 开发工具包 JDK 和集成开发环境 IDE 比如 Eclipse 或 Int
  • python报错:RuntimeError

    python报错 RuntimeError fails to pass a sanity check due to a bug in the windows runtime这种类型的错误 这种错误原因 1 当前的python与numpy版本
  • Gradle项目运行报错Could not find method XXX及解决方案

    Could not find method testCompile for arguments dependencies testCompile group junit name junit version 4 13 1 原因是较新版的gr
  • 计算机截屏窗口快捷键,电脑截屏的快捷键是什么

    电脑截屏的快捷键是什么 截屏是一种截取图片或文字的途径 也是一种计算机运用技术 通过这种技术可以从网上截取下自己感兴趣的文章图片供自己使用观看 可以帮助人们更好的去理解使用知识 是一种人人都能使用并且学会方法 可以通过一些软件实现截屏功能
  • XSS-Game level 7

    第七关过滤了 script 但未过滤尖括号 lt gt 使用双写绕过 源码中过滤了大小写 script 以及一些属性 并将参数拼接到 value 值 使用 htmlspecialchars 过滤标签 但未重新赋值给 str 所以不会产生影响
  • Google Play Services Location:显示位置的地址

    在前面的blog中讲述了怎样获取最后已知位置和接收位置更新后如何从location对象中获取用户的位置 包含经纬度的坐标 虽然纬度和经度是用于计算距离或显示地图的位置 在许多情况下 位置的地址是更为有用的 例如 如果你想让你的用户知道他们是
  • Wireshark零基础使用教程(超详细)

    Wireshark零基础使用教程 一 Wireshark是什么 二 Wireshark抓包原理 三 Wireshark安装入门 1 选择网卡 2 停止抓包 3 保存数据 四 界面介绍 五 基础操作 1 调整界面大小 2 设置显示列 1 添加
  • 一个仿 github for windows 及 windows 8 的进度条

    https github com wly2014 ProgressBar 转载于 https www cnblogs com eustoma p 4470396 html
  • dubbo_异常Exception in thread "main" org.springframework.beans.factory.UnsatisfiedDependencyException

    Exception in thread main org springframework beans factory UnsatisfiedDependencyException Error creating bean with name
  • BES2300x笔记(27) -- 声道设定与声道切换

    哈喽大家好 这是该系列博文的第二十七篇 篇 lt lt 系列博文索引 快速通道 gt gt 一 前言 前几天 有道友私信问到 BES2300如何进行声道设定 想通过硬件进行固定 那么 这一篇我们就讲讲BES平台有关声道的设定 以及如何进行硬
  • 计算机程序设计语言教案,计算机程序设计(C语言)课程教案讲稿概要.doc

    计算机程序设计 C 语言 课程教案 讲稿 教师姓名 纪澍琴 学院 部 中心 信息传播工程学院 教研室 实验室计算机基础教研室 2008年3月 长春工业大学课程教案 讲稿 与课程有关的信息 教师编号 000361 课程名称 计算机程序设计 C
  • android listview 图片闪烁,listView异步加载图片导致图片错位、闪烁、重复的问题的解决...

    androidListView是android中重要的控件 几乎每一个项目都会用到 但是在使用中我们避免不 了会出现一些问题 包括一些滑动事件的处理 例如 ListView中嵌套scrollView 容易出现listView 展现数据不全的
  • adb关闭手机系统自动更新

    下载adb工具 https mclub lenovo com cn forum php mod attachment aid NDg5ODc1Nnw4MWRhZDE4OHwxNjU0NTI0OTY1fDB8NzgzNzg5OQ 3D 3D
  • Android jni ndk crash c++bug定位

    最近遇到了一个底层c 库的问题 然而看不到是在哪里报错的 有一个方法就是用 ndk stack的方法 在cmd里面切换到adb 在电脑上的目录 然后输入adb logcat ndk stack sym F whl MyApp 替换为你的项目
  • 【C++】递归

    1 什么是递归 程序调用自身的编程技巧称为递归 递归做为一种算法在程序设计语言中广泛应用 一个过程或函数在其定义或说明中有直接或间接调用自身的一种方法 它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解 递归策略只需