当前课程知识点:基于Linux的C++ >  第四讲 算法 >  4.5 递归算法(一) >  LinuxCPP0405

返回《基于Linux的C++》慕课在线视频课程列表

LinuxCPP0405在线视频

LinuxCPP0405

下一节:LinuxCPP0406

返回《基于Linux的C++》慕课在线视频列表

LinuxCPP0405课程教案、知识点、字幕

接下来一小节就是我们要讨论

一个非常重要的一种算法

我们称它为递归算法

我们知道 在数学上求一个数列的时候

经常会使用一种所谓的通项公式

可是还有的时候

我们给的不是通项公式 叫递推公式

数学上是非常常见的

比如阶乘函数 1!是1

然后n!就是n乘上(n-1)!

这就是一个递推的公式

n的阶乘是怎么算呢

是n-1的阶乘算出来以后

然后乘上n装配上来的

n-1的阶乘怎么算呢 同样的原则算

这个就叫递推公式

这在数学上非常非常常见

计算机编程的时候就必须考虑这一点

怎么去实现它

还有第二种你看斐波那契数列

斐波那契数列f(1)、f(2)等于1

然后f(n)就等于f(n-1)加上f(n-2)

注意到所有的数学上的

递推公式都是分段函数

总有一个初始表达式

那么计算过程呢

你就得知道 问题规模逐步简化

如果设f(n)括号里面的那个n值

是问题规模的话

f(n-1) 那个n-1就是它的问题规模

n-1比n小吧

那就叫问题规模逐渐简化

递推都是这样工作的

所以写递归程序的时候

同学们要特别记住的

就是我们这里面实际上包括两部分

第一个我要完成一个递推

把f(n-1)分解成f(n-2)、f(n-3)

n!分解成(n-1)!

(n-1)!分解成(n-2)!

必须按照这个过程逐步分解问题

让这个问题越来越简单

然后第二步我要回归

我用1!装配出2!来

用2!装配出3!

这就是递推加回归

合在一起我们就称它为递归

那么我们怎么实现像这样的阶乘函数

和递归函数呢 先看阶乘函数

现在给出两种实现策略

一种叫循环策略

一种叫做递归实现的策略

先看循环策略

那么很简单 我定义一个i

定义了一个结果result

把result初始化成1

i初始值初始成0 然后while

++i之后 i<=n

把这个i值乘到result上去

把1乘上去 2乘上去 一直乘到n

全乘上去以后结果就是阶乘

函数就结束了

你看我的代码 四行 很短吧

你会觉得这个代码很容易理解

那么如果我们用递归怎么来实现呢

在这里面我也定义一个result

我就按照递推那个式子

阶乘这个定义来

如果n是1

result就赋值为1

else result就赋值为n乘上(n-1)!

return result结果就行了

我(n-1)!怎么来的

这就涉及到递归函数

最重要的一个地方了 你注意看

我这个函数名字叫GetFactorial

求这个阶乘 传的参数是n

那么我怎么求(n-1)!呢 就调用它呗

这个函数不就是算阶乘了吗

一个函数通过直接或间接的手段

调用自身的动作就叫递归

这样的函数就叫递归函数

我们看第二个

刚才那个例子看得不明显

不管使用递归实现还是循环实现

它的代码其实都蛮短的

所以你感觉不到它们之间的差异

那我们看斐波那契数列

这个其实就特别明显

如果使用递归实现的话

其实策略跟刚才非常非常相似

就是这么写的 if(n==2||n==1)

return 1

else return GetFibonacci(n-1) + GetFibonacci(n-2)

这里面会产生两次递归 没关系啊

按照它的定义就应该这么来

那我们这么写就行了

很典型吧 四行代码 解决了

回到循环实现 如果if(n==2||n==1)

结果是很明确的return 1

接下来我们要做f3

有了f3我才能装配出f4

所以要一个循环来装配

我要把1给f2 然后把1给f1

然后写一个for循环 开始装配

f3赋值为f1加f2

然后把f2作为新的f1

刚刚算出来f3作为新的f2去算新的f3

那其实就是变成f4了

我们就不断往下循环 依次迭代

去找它的fn

按照这样的方式去实现的话

这个代码量

明显比我们递归实现代码量要大很多

这是非常重要的一个地方

就是对于同样一个问题来求解的话

使用递归实现往往要比

使用循环实现的代码量要短很多

我们可以对循环和递归

做一些简单的比较

首先呢循环是用显示的循环结构

来执行我们的代码段

而递归是使用函数调用

来重复执行代码段 这是第一点

第二点 循环是在

满足终止条件的时候终止执行

递归 要到退化到最简单的情形的时候

它才能够终止执行

第三个 循环的重复

是在当前迭代执行完毕以后

递归的重复在发现到它对同名的函数

也就是它自身的一次函数调用的时候

第四个 循环也好 递归也好

都可能在程序中藏着程序错误

所以你检查的时候都是需要特别小心的

最后一个 同学们要特别记得

理论上任何一个递归程序代码

都可以使用循环来重新实现

这都没有问题

但是我要跟同学们特别强调

一 大部分程序递归算法

非常非常的短小精悍 简单 代码量短

第二 有一些函数

当你使用循环来实现的时候

当你没有发现技巧的时候

用循环来实现其实是相当相当困难的

但使用递归

一旦你养成了正确的递归思考方法以后

递归的实现策略

其实是非常简单和直接的

我们来看一个例子

递归函数的调用栈框架

当它调用的时候会发生什么事情呢

我们就用刚才那个

阶乘函数的例子来展示

这个里面定义了一些函数原型

写在了main函数前面

接下来在main函数里面得到这个整数

然后就调用GetFactorial去算它的阶乘

具体的实现就是刚才那一段代码

这个我就不解释了

关键的问题是

我们关注函数栈框架的调整

一个main函数里面 有n 又有result

我们假设n的值是3

3!能算吗

调用GetFactorial这个函数去算3!

发现3!没法直接算

它必须用3乘上2!去装配

所以它会产生一次递归调用

当它产生这个递归调用的时候

第一次调用GetFactorial这个函数

并没有结束

它就会调用第二次GetFactorial这个函数

n的值就会传进去2

这就是第二次函数调用

你会看到第二次调用

GetFactorial函数的栈框架

覆盖了第一次调用的栈框架

递归调用发生的时候

就是按照这个模式

递归调用栈框架不断覆盖上去

然后不断再重新消失这个过程

这就是我们函数调用栈框架

2!能够直接算吗 还不能

我们必须继续调用GetFactorial

第三次调用GetFactorial

n传进去1

我现在能够简单直接得到阶乘值吗

是的 1!为1 这是我们的定义

它的初始表达式里面

就包含了这样一个定义

那么 我们就直接写result赋值为1

正常情况下来讲

这个函数就可以直接return了

GetFactorial这个函数栈框架就会消失

就是第三次调用的

这个栈框架就会消失

然后把1的结果

传给第二次调用的GetFactorial

然后一乘2 结果就出来了

结果出来它就会消失

然后第一次调用的GetFactorial

这个函数栈框架就出来了

乘上3 结果是6

然后它的结果也出来了

它的栈框架也消失

然后main就得到结果6

3!为6

这就是递归调用的时候函数栈框架

从刚才那个演示里面

你就看得非常清楚

对于一个递归函数GetFactorial

它是一次一次调用

最先调用的那一次最后消失

所以这就叫栈嘛

满足先进后出规则的一种特殊结构

我们的函数调用就是这么工作的

我们计算机的原理就是这么定的

所以我们整个递归调用

就必须按照这样方式进行工作

这是我们函数调用栈框架

基于Linux的C++课程列表:

第一讲 C/C++基本语法元素

-1.1 提纲

--LinuxCPP0101

-1.2 程序设计的基本概念

--LinuxCPP0102

-1.3 简单C/C++程序介绍

--LinuxCPP0103

-1.4 程序设计的基本流程

--LinuxCPP0104

-1.5 基本语法元素

--LinuxCPP0105

-1.6 程序设计风格

--LinuxCPP0106

-1.7 编程实践

--LinuxCPP0107

-第一讲 C/C++基本语法元素--编程实践提交入口

第二讲 程序控制结构

-2.1 提纲

--LinuxCPP0201

-2.2 结构化程序设计基础

--LinuxCPP0202

-2.3 布尔数据

--LinuxCPP0203

-2.4 分支结构

--LinuxCPP0204

-2.5 break语句

--LinuxCPP0205

-2.6 循环结构

--LinuxCPP0206

-2.7 编程实践

--LinuxCPP0207

-第二讲 程序控制结构--编程实践提交入口

第三讲 函数

-3.1 提纲

--LinuxCPP0301

-3.2 函数声明、调用与定义

--LinuxCPP0302

-3.3 函数调用栈框架

--LinuxCPP0303

-3.4 编程实践

--LinuxCPP0304

-第三讲 函数--编程实践提交入口

第四讲 算法

-4.1 提纲

--LinuxCPP0401

-4.2 算法概念与特征

--LinuxCPP0402

-4.3 算法描述

--LinuxCPP0403

-4.4 算法设计与实现

--LinuxCPP0404

-4.5 递归算法(一)

--LinuxCPP0405

-4.6 递归算法(二)

--LinuxCPP0406

-4.7 容错与计算复杂度

--LinuxCPP0407

-4.8 编程实践

--LinuxCPP0408

-第四讲 算法--编程实践提交入口

第五讲 程序组织与开发方法

-5.1 提纲

--LinuxCPP0501

-5.2 库与接口

--LinuxCPP0502

-5.3 随机数库(一)

--LinuxCPP0503

-5.4 随机数库(二)

--LinuxCPP0504

-5.5 作用域与生存期

--LinuxCPP0505

-5.6 典型软件开发流程(一)

--LinuxCPP0506

-5.7 典型软件开发流程(二)

--LinuxCPP0507

-5.8 编程实践

--LinuxCPP0508

-第五讲 程序组织与开发方法--编程实践提交入口

第六讲 复合数据类型

-6.1 提纲

--LinuxCPP0601

-6.2 字符

--LinuxCPP0602

-6.3 数组(一)

--LinuxCPP0603

-6.4 数组(二)

--LinuxCPP0604

-6.5 结构体

--LinuxCPP0605

-6.6 编程实践

--LinuxCPP0606

-第六讲 复合数据类型--编程实践提交入口

第七讲 指针与引用

-7.1 提纲

--LinuxCPP0701

-7.2 指针基本概念

--LinuxCPP0702

-7.3 指针与函数

--LinuxCPP0703

-7.4 指针与复合数据类型(一)

--LinuxCPP0704

-7.5 指针与复合数据类型(二)

--LinuxCPP0705

-7.6 字符串

--LinuxCPP0706

-7.7 动态存储管理(一)

--LinuxCPP0707

-7.8 动态存储管理(二)

--LinuxCPP0708

-7.9 引用

--LinuxCPP0709

-7.10 编程实践

--LinuxCPP0710

-第七讲 指针与引用--编程实践提交入口

第八讲 链表与程序抽象

-8.1 提纲

--LinuxCPP0801

-8.2 数据抽象(一)

--LinuxCPP0802

-8.3 数据抽象(二)

--LinuxCPP0803

-8.4 链表(一)

--LinuxCPP0804

-8.5 链表(二)

--LinuxCPP0805

-8.6 链表(三)

--LinuxCPP0806

-8.7 链表(四)

--LinuxCPP0807

-8.8 函数指针(一)

--LinuxCPP0808

-8.9 函数指针(二)

--LinuxCPP0809

-8.10 抽象链表(一)

--LinuxCPP0810

-8.11 抽象链表(二)

--LinuxCPP0811

-8.12 编程实践

--LinuxCPP0812

-第八讲 链表与程序抽象--编程实践提交入口

第九讲 类与对象

-9.1 提纲

--LinuxCPP0901

-9.2 程序抽象与面向对象

--LinuxCPP0902

-9.3 类类型

--LinuxCPP0903

-9.4 对象(一)

--LinuxCPP0904

-9.5 对象(二)

--LinuxCPP0905

-9.6 类与对象的成员(一)

--LinuxCPP0906

-9.7 类与对象的成员(二)

--LinuxCPP0907

-9.8 类与对象的成员(三)

--LinuxCPP0908

-9.9 继承(一)

--LinuxCPP0909

-9.10 继承(二)

--LinuxCPP0910

-9.11 继承(三)

--LinuxCPP0911

-9.12 多态(一)

--LinuxCPP0912

-9.13 多态(二)

--LinuxCPP0913

-9.14 编程实践

--LinuxCPP0914

-第九讲 类与对象--编程实践提交入口

第十讲 操作符重载

-10.1 提纲

--LinuxCPP1001

-10.2 四则运算符重载(一)

--LinuxCPP1002

-10.3 四则运算符重载(二)

--LinuxCPP1003

-10.4 关系与下标操作符重载

--LinuxCPP1004

-10.5 赋值操作符重载(一)

--LinuxCPP1005

-10.6 赋值操作符重载(二)

--LinuxCPP1006

-10.7 赋值操作符重载(三)

--LinuxCPP1007

-10.8 赋值操作符重载(四)

--LinuxCPP1008

-10.9 赋值操作符重载(五)

--LinuxCPP1009

-10.10 流操作符重载(一)

--LinuxCPP1010

-10.11 流操作符重载(二)

--LinuxCPP1011

-10.12 流操作符重载(三)

--LinuxCPP1012

-10.13 操作符重载总结

--LinuxCPP1013

-10.14 编程实践

--LinuxCPP1014

-第十讲 操作符重载--编程实践提交入口

第十一讲 泛型编程

-11.1 提纲

--LinuxCPP1101

-11.2 泛型编程概览

--LinuxCPP1102

-11.3 异常处理机制(一)

--LinuxCPP1103

-11.4 异常处理机制(二)

--LinuxCPP1104

-11.5 运行期型式信息(一)

--LinuxCPP1105

-11.6 运行期型式信息(二)

--LinuxCPP1106

-11.7 模板与型式参数化

--LinuxCPP1107

-11.8 题外话:术语翻译

--LinuxCPP1108

-11.9 泛型编程实践(一)

--LinuxCPP1109

-11.10 泛型编程实践(二)

--LinuxCPP1110

-11.11 泛型编程实践(三)

--LinuxCPP1111

-11.12 泛型编程实践(四)

--LinuxCPP1112

-11.13 泛型编程实践(五)

--LinuxCPP1113

-11.14 泛型编程实践(六)

--LinuxCPP1114

-11.15 泛型编程实践(七)

--LinuxCPP1115

-11.16 泛型编程实践(八)

--LinuxCPP1116

-11.17 泛型编程实践(九)

--LinuxCPP1117

-11.18 泛型编程实践(十)

--LinuxCPP1118

-11.19 编程实践

--LinuxCPP1119

-第十一讲 泛型编程--编程实践提交入口

第十二讲 Linux系统编程基础

-12.1 提纲

--LinuxCPP1201

-12.2 程序执行环境(一)

--LinuxCPP1202

-12.3 程序执行环境(二)

--LinuxCPP1203

-12.4 程序执行环境(三)

--LinuxCPP1204

-12.5 程序执行环境(四)

--LinuxCPP1205

-12.6 输入输出(一)

--LinuxCPP1206

-12.7 输入输出(二)

--LinuxCPP1207

-12.8 文件系统

--LinuxCPP1208

-12.9 设备

--LinuxCPP1209

-12.10 库(一)

--LinuxCPP1210

-12.11 库(二)

--LinuxCPP1211

-12.12 makefile文件(一)

--LinuxCPP1212

-12.13 makefile文件(二)

--LinuxCPP1213

-12.14 makefile文件(三)

--LinuxCPP1214

-12.15 编程实践

--LinuxCPP1215

-第十二讲 Linux系统编程基础--编程实践提交入口

第十三讲 进程编程

-13.01 提纲

--LinuxCPP1301

-13.02 进程基本概念

--LinuxCPP1302

-13.03 信号

--LinuxCPP1303

-13.04 进程管理(一)

--LinuxCPP1304

-13.05 进程管理(二)

--LinuxCPP1305

-13.06 进程管理(三)

--LinuxCPP1306

-13.07 进程间通信(一)

--LinuxCPP1307

-13.08 进程间通信(二)

--LinuxCPP1308

-13.09 进程间通信(三)

--LinuxCPP1309

-13.10 进程间通信(四)

--LinuxCPP1310

-13.11 进程池

--LinuxCPP1311

-13.12 编程实践

--LinuxCPP1312

-第十三讲 进程编程--编程实践提交入口

第十四讲 线程编程

-14.1 提纲

--LinuxCPP1401

-14.2 线程基本概念

--LinuxCPP1402

-14.3 线程管理(一)

--LinuxCPP1403

-14.4 线程管理(二)

--LinuxCPP1404

-14.5 线程管理(三)

--LinuxCPP1405

-14.6 线程管理(四)

--LinuxCPP1406

-14.7 线程同步机制(一)

--LinuxCPP1407

-14.8 线程同步机制(二)

--LinuxCPP1408

-14.9 C++11线程库(一)

--LinuxCPP1409

-14.10 C++11线程库(二)

--LinuxCPP1410

-14.11 C++11线程库(三)

--LinuxCPP1411

-14.12 C++11线程库(四)

--LinuxCPP1412

-14.13 C++11线程库(五)

--LinuxCPP1413

-14.14 编程实践

--LinuxCPP1414

-第十四讲 线程编程--编程实践提交入口

第十五讲 网络编程

-15.1 提纲

--LinuxCPP1501

-15.2 Internet网络协议

--LinuxCPP1502

-15.3 套接字(一)

--LinuxCPP1503

-15.4 套接字(二)

--LinuxCPP1504

-15.5 编程实践

--LinuxCPP1505

-第十五讲 网络编程--编程实践提交入口

课程文档

-课程PDF文件

LinuxCPP0405笔记与讨论

也许你还感兴趣的课程:

© 柠檬大学-慕课导航 课程版权归原始院校所有,
本网站仅通过互联网进行慕课课程索引,不提供在线课程学习和视频,请同学们点击报名到课程提供网站进行学习。