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

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

LinuxCPP0406在线视频

LinuxCPP0406

下一节:LinuxCPP0407

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

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

这个例子叫汉诺塔问题

有了一个正确的递归思考方法以后

这个算法其实实现起来并不复杂

但是同学们要特别记住

如果使用一个非递归的算法来实现

那么如果你不知道它的实现技巧

硬生生去模拟栈框架调整过程的话

那么这个代码写起来

可以说是非常非常困难的

汉诺塔这个题什么意思呢

就是我们这里面有

分别命名为x和y、z的

这三个名字的塔座

然后还有一堆盘子

这些盘子大小都不一样

从小到大编号

1号、2号、……n号

然后我要搬这个盘子

x轴上一堆盘子移动到z上去

基本的规则就是:

一 这些盘子一次只能动一个

第二个 圆盘呢

x、y、z三个上面哪个都可以放

第三个 在任何时候

不能将大盘子放到小盘子的上面

这就这么三个规则

然后我们想怎么把这堆盘子

从x轴移动到z轴

那么我们要分析这个问题

该怎么去解决

我们现在要问自己俩问题

第一个 是不是存在一种最简单的情况

在这种情况下面问题很容易解决

第二个 是不是可以将原始问题

分解成性质相同

但是问题规模较小的问题

然后新问题的解答对原始问题的解答

具有重要的指导意义

这个就是我们要写递归算法的时候

同学们需要特别记住的两个地方

同学们回忆一下

怎么用数学归纳法来证明题

我是首先证明n等于1的时候成立

然后假设n=k成立的时候

证明n=k+1的时候成立

你怎么去找到从n=k成立的时候

到n=k+1成立的那个过程呢

先看看n=1的时候是什么样子

再看看n=2是什么样子

再看看n=3的时候是什么样子

看看能不能找到它的拼装规律嘛

这就是最重要一个地方

一个盘子我怎么搬呢 简单 直接挪

那我们想我怎么找n个盘子的规律呢

那我们就看两个盘子怎么办呢

我第一个盘子放到y轴上面临时放一放

然后第二盘子就能放到z轴了

就能挪过去了

然后把第一个盘子再从y轴上面

挪到z轴上放上去不就完了吗

这是两个盘子

那么三个盘子我怎么搬 就琢磨了

这其实应该把1号盘放到z轴

2号盘放到y轴

然后把1号盘再从z轴搬到y轴

这样的话两个盘就挪动到y轴上去了

按照这个模式

1号盘和2号盘合在一起作为一个整体

这个就是递归算法的实现策略

有了这个解决方案

那我们看汉诺塔演示动画

就完成了 四个盘子的完全的搬动

据此就可以写出汉诺塔的伪代码

一个MoveHanoi搬动这个汉诺塔

这里面带了4个参数

第一个参数是盘子有多少个 n

后面跟着3个参数就是三个轴的标志

我们定义枚举型HANOI

三个轴一个是from 一个是temp一个是to

一个是原始轴 一个是中转轴

一个是目标轴

我们写我们伪代码 if(n==1)

如果就一个盘子

那我就将这个盘子从from搬到to完事了

否则的话呢我就将它前面n-1个盘子

从from搬到temp临时放一放

这个时候的中转轴就是to

然后我就搬动第n号盘

从from搬动到to

在此之后将临时放一放的n-1个盘子

从temp搬到to

这个时候我们就以from为中转

搬过去就完了

这个就是汉诺塔这一道题的伪代码

你一看 是不是很简单

几行代码 非常短小

我们看看整个这个程序的具体实现

这个程序我们定义了一个枚举型HANOI

它的文字分别代表了

三个圆柱的代号:X、Y、Z

特别注意 因为要输出这个信息

模拟搬盘子的动作

在屏幕上全部打印出来

所以要有一个MovePlate

模拟几号盘从什么地方搬到什么地方

因为枚举型是不能够直接输出的

你直接输出的话

其实最终会转化成整数去输出

所以我们写一个函数ConvertHanoiToChar

把它转化成字符去输出

main函数的实现就是得到一个n值

然后移动汉诺塔

n个盘子 x、y、z三个轴

这是我们的程序代码

前面这两个都是我们原来都实现过的

就不解释了

然后ConvertHanoiToChar

使用一个switch语句

把这个枚举文字转换成字符

然后MovePlate就是单一一个盘子的搬动

我们把这个from转换成一个对应的字符

把to转换成一个对应的字符

然后我们就输出n号盘

从什么地方到什么地方

然后是MoveHanoi真实的代码

就是汉诺塔递归实现的真实代码 if(n==1)

把1号盘从from搬到to

如果不是最简单情形

那我就else MoveHanoi

n-1个盘子

从from以to为中转搬到temp

然后搬动第n号盘 从from搬到to

再然后把刚才搬到一边去的

n-1一个盘子

从temp以from为中转搬到to

这就是我们MoveHanoi递归函数的实现

确确实实看上去很简洁

程序代码就这么多

这就完成了全部程序

在实现这个递归函数的时候

同学们一定要记住

要养成最基本的递归信任

有的时候 初学者经常会觉得

这个递归函数能行吗

总是怀有疑虑 我要特别强调

递归它一定是能够正确工作的

养成最基本的递归信任

不用去怀疑递归工作基本原理

递归信任里面我们能够注意到六个问题

第一个

递归实现是不是检查了最简单情形

大量递归程序错误都是因为

最简单情形你没有检查到导致的

大部分情况下面

你的递归程序应该是if开头的

如果不是 你就要特别小心

这个程序十之八九就是错的

特别注意这一条

最简单情形你一定要有

第二个

这个最简单情形你是不是解决了

有的时候最简单情形我们可能没解决

当时给出问题解决方案不对

尤其同学们需要注意到

当我们解决最简单情形的时候

你不能调用递归

不是说它真地不能调用递归

你可以调用与它完全无关的一个递归

那个没问题

但是一定不能够再通过直接

或间接手段再去调用自己

最简单情形一定是不能这么干的

大部分递归程序

对于最简单的情形来讲

值都是简单直接设的过程

非常非常简单的语句

一般来讲都是这样

第三个 递归算法分解动作

是不是让问题更简单

这是非常重要的

问题规模不是越来越简单

而是问题规模越来越复杂了

这样搞下去 就没完没了了

你怎么能够得到结论呢

你是得不到结论的

第四个 问题简化过程

能不能够回归到最简单情形

还是遗漏一些情况

回不去就不成

这是非常重要的地方 特别要小心

也就是初始条件要写全了

第五个

子问题是不是和原始问题完全一致

你分解出来一个子问题

一定要和原来问题完全一致才行

如果它不完全一致 肯定就不成

相当于你偷换了概念

新的问题和原来的问题它就不一样了

它不是一个问题

就不能够用同样的方案去做

这个地方是一定要注意的

这是第五个

第六个 组装

使用递归实现 问题被你化简了

子问题的解也被求出来

结果组装给搞错了

那么这个问题的答案肯定也是不对的

就比如说刚才汉诺塔问题

它组装的过程是很明显的

要先把n-1个盘子

搬到临时轴那个地方放一放

然后把n号盘搬到你的目标轴Z

然后再把n-1的盘子

从临时中转轴搬到Z上

这是一个非常明确的过程

这就是一个组装 先一次递归调用

然后搬动最后一个大号牌

再来一次递归调用

如果你颠倒了一个次序

它就不对了

总体上来讲 在编写递归程序的时候

这六个地方要时刻保持警觉

确保它们都没有问题

你的递归函数就一定能够正确工作

这个就叫递归信任

基于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文件

LinuxCPP0406笔记与讨论

也许你还感兴趣的课程:

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