Task03:栈与递归(2天)
内容设计:光城、LeoLRH、肖然
内容组织:老马的程序人生、super、高永伟
审稿人:光城
定位人群:有编程语言基础知识,熟悉面向对象程序设计
时间安排:9天,每天平均花费时间3小时-5小时不等,根据个人学习接受能力强弱有所浮动
任务简介:
Task01:数组(1天)
Task02:顺序表和链表(2天)
Task03:栈与递归(2天)
Task04:队列(2天)
Task05:字符串(2天)
详细安排:
https://github.com/datawhalechina/team-learning/blob/master/数据结构与算法(上)/学习任务.md
栈是我们经常使用的一种数据结构,如下图所示,手枪发射子弹的顺序与子弹压入弹夹的顺序是相反,即后压入弹夹的子弹先发射出来。
比如我们使用的Word、Excel、Photoshop等软件系统中的撤销操作,也是栈的具体应用,最后做的操作,一定是最先撤销的。下面我们就来详细介绍“栈”这种数据结构。
1. 栈的定义与操作
1.1 栈的定义
插入(入栈)和删除(出栈)操作只能在一端(栈顶)进行的线性表。即先进后出(First In Last Out)的线性表。
例1 :线性表(a0,a1,...,an)
进栈与出栈演示。
如上所示,栈有两种实现一种是顺序栈一种是链栈,这两种实现方式有什么区别呢,其实与顺序表和链表是一样的:
顺序栈是静态分配的但是链栈是动态分配的,所以比较起来链栈对于空间的利用率更高。因为顺序栈可能申请了较大的空间但是并没有全部都存储元素。
顺序栈虽然不用存储指针相比较链栈来说较为节省内存空间,但是链栈却可以将零碎的内存空间利用起来。
而且对于存储量未知的情况下,链栈更加适合,因为链栈通常不会出现栈满的情况。
对于顺序表和链表来说,链表对于插入和删除效率更高,顺序表对于查找效率更高。但是对于栈来说只能在栈顶进行操作,所以无法体现链表的效率更高。
1.2 栈的操作
入栈操作:将数据元素值插入栈顶。
出栈操作:移除栈顶的数据元素。
是否为空:判断栈中是否包含数据元素。
得到栈深:获取栈中实际包含数据元素的个数。
清空操作:移除栈中的所有数据元素。
获取栈顶元素。
以下代码为C#
版本:
using System;
2. 栈的存储与实现
2.1 顺序存储(顺序栈)
顺序栈:利用顺序表实现的栈。
实现:
以下代码为C#
版本:
using System;
2.2 链式存储(链栈)
链栈:利用单链表实现的栈。
实现:
以下代码为C#
版本:
using System;
3. 递归
如果一个函数在内部调用自身本身,这个函数就是递归函数。
Sample01:求n的阶乘
n! = 1 x 2 x 3 x ... x n
循环:
以下代码为Python
版本:
5
递归:
以下代码为Python
版本:
def factorial(n):
Samp02:斐波那契数列
f(n)=f(n-1)+f(n-2), f(0)=0 f(1)=1
循环:
以下代码为Python
版本:
0
递归:
以下代码为Python
版本:
def recur_fibo(n):
注意:设置递归的层数,Python默认递归层数为 100
import sys
Sample03:汉诺塔问题
汉诺塔问题源于印度一个古老传说的益智玩具。大梵天创造世界的时候做了三根金刚石柱子,在一根柱子上从下往上按照大小顺序摞着 64 片黄金圆盘。大梵天命令婆罗门把圆盘从下面开始按大小顺序重新摆放在另一根柱子上。并且规定,在小圆盘上不能放大圆盘,在三根柱子之间一次只能移动一个圆盘。
如果我们要思考每一步怎么移可能会非常复杂,但是可以将问题简化。
我们可以先假设除 a 柱最下面的盘子之外,已经成功地将 a 柱上面的 63个盘子移到了 b 柱,这时我们只要再将最下面的盘子由 a 柱移动到 c 柱即可。
当我们将最大的盘子由 a 柱移到 c 柱后,b 柱上便是余下的 63 个盘子,a 柱为空。因此现在的目标就变成了将这 63 个盘子由 b 柱移到 c 柱。这个问题和原来的问题完全一样,只是由 a 柱换为了 b 柱,规模由 64 变为了 63。因此可以采用相同的方法,先将上面的 62 个盘子由 b 柱移到 a 柱,再将最下面的盘子移到 c 柱。
以此内推,再以 b 柱为缓冲,将 a 柱上面的 62 个圆盘最上面的 61 个圆盘移动到 b 柱,并将最后一块圆盘移到 c 柱。
我们已经发现规律,我们每次都是以 a 或 b 中一根柱子为缓冲,然后先将除了最下面的圆盘之外的其它圆盘移动到辅助柱子上,再将最底下的圆盘移到 c 柱子上,不断重复此过程。
这个反复移动圆盘的过程就是递归,例如我们每次想解决 n 个圆盘的移动问题,就要先解决(n-1)个盘子进行同样操作的问题。
于是可以编写一个函数,move(n, a, b, c)。可以这样理解:move(盘子数量, 起点, 缓冲, 终点)。
1. a 上只有一个盘子的情况,直接搬到 c,代码如下:
if n ==
2. a 上不止有一个盘子的情况:
首先,需要把 n-1 个盘子搬到 b 柱子缓冲。打印出的效果是:a --> b。
1, a, c, b)
再把最大的盘子搬到 c 柱子,也是最大尺寸的一个。打印出:a-->c。
1, a, b, c)
最后,把剩下 b 柱的 n-1 个盘子搬到 c 上,此时缓冲变成了起点,起点变成了缓冲。
1, b, a, c)
利用 Python 实现汉诺塔问题
0
利用 C# 实现汉诺塔问题
class Program
{
4. 练习参考答案
车辆重排的程序代码,如下(C#
版本):
using System;
我是终身学习者“老马”,一个长期践行“结伴式学习”理念的中年大叔。
我崇尚分享,渴望成长,于2010年创立了“LSGO软件技术团队”,并加入了国内著名的开源组织“Datawhale”,也是“Dre@mtech”、“智能机器人研究中心”和“大数据与哲学社会科学实验室”的一员。
愿我们一起学习,一起进步,相互陪伴,共同成长。
后台回复「搜搜搜」,随机获取电子资源!
欢迎关注,请扫描二维码: