高中信息技术 竞赛班数据结构专项培训教程 03栈和队列教案-人教版高中全册信息技术教案
- 格式:doc
- 大小:125.00 KB
- 文档页数:18
xxxx学院教案首页xxxx学院教案附页第一学时(栈的定义、常用操作与顺序存储的实现)一、情景导入(1)引出栈的概念由线性表引出栈:栈是线性表的一种,只是在操作上,与线性表有所差别。
如果没有道具,借用课本,粉笔盒或学生自主进行展示。
(2)进入主题,讲解栈的特点与线性表进行类比,栈与基础线性表的差别,在于它的操作受限、取数的方式不如线性表自由。
可以结合图3-1中的图示,引导学生回想存取碗时的具体操作,帮助学生领悟栈的特点。
图3-1 一摞碗二、知识讲解(1)栈的特点结合图3-2中栈的结构图,对栈的操作原则——后进先出,进行讲解。
图3-2 栈的结构图(2)栈的常用操作栈的常用操作如下:•创建栈(初始化栈)•判断栈是否为空•进栈•出栈•获取栈顶元素•获取栈的长度•销毁栈在对栈的原则进行讲解之后,对栈中常用操作进行总结。
(3)栈的顺序存储实现栈是线性表的一种,顺序存储的栈是一种顺序表。
在知识点(2)提到的操作中,进栈和出栈是可以展示出栈特点的特色操作,可以结合图示,对这两种操作的实现进行详细说明;此外,简单叙述栈中其余功能的实现方法。
在讲解完顺序栈中的各种操作之后,结合书中例3-1给出的代码,带领学生掌握栈的实现方法。
第二学时(栈的链式存储实现)一、知识回顾(1)对上节课留的作业进行答疑。
(2)回顾总结上节课的内容,引出本节课主题。
上个学时讲解了栈的定义、栈的特点与栈的顺序实现,本学时来探讨栈的链式存储实现。
二、知识讲解(1)链栈的数据结构定义链栈是一种链表,与顺序栈相同,链栈在操作时也受到限制,遵循“后进先出”的原则。
结合链表的数据结构定义,引导学生完成链栈的数据结构定义。
(2)链栈的实现链栈的存储方式与链表相同,操作原则与顺序栈相同。
从这两点出发进行分析,引导学生找到链栈实现的思路,然后给学生留出一定时间,由学生自主分析巩固链栈操作的实现方法,之后结合学生自主实现链栈时遇到的问题,对链栈进行讲解。
(用栈实现四则运算)三、情境引入通过计算机的算术运算功能,引出本学时的主题:计算机的基本功能大多都是基于对数据的操作,给出一个运算式,计算机能迅速计算出结果,若运算时有误,如运算式“1+3*(2+5”,右边少了一个“)”,编绎器会立刻检查出错误并报告,那么计算机是如何做到的呢?藉由以上问题,引出逆波兰表达式。
栈和队列教案(总13页) --本页仅作为文档封面,使用时请直接删除即可----内页可以根据需求调整合适字体及大小--教案课程名称:数据结构(C语言版)授课班级:技校二年级学生授课学时:1学时授课章节:第三章栈和队列课型:理论课任课教师:***图2 栈的示意图从图2中可以看出第一个进栈的a1为栈底元素,最后一个进栈的an为栈顶元素,进栈和出栈也是同一个方向。
这也是最基本的栈的示意图。
需要同学们熟知。
其实,要解决这个出站问题就离不开我们今天将要学习的进栈、出栈技术,这节课我们将从如下三个方面来掌握它:栈的定义、栈的表示和实现、栈的应用与练习。
其中栈的表示与实现是本节的重点。
一、抽象数据类型栈的定义(6分钟)点。
二、介绍栈的定义类型(8分钟)ADT Stack{数据对象:D={ a i | a i ∈ElemSet, i=1,2,...,n, n≥0 }数据关系:R1={ <a i-1, a i >| a i-1, a i∈D, i=2,...,n }约定a n端为栈顶,a1 端为栈底。
基本操作:InitStack(&S)操作结果:构造一个空栈 S。
DestroyStack(&S)初始条件:栈 S 已存在。
操作结果:栈 S 被销毁。
StackEmpty(S)初始条件:栈 S 已存在。
操作结果:若栈 S 为空栈,则返回TRUE,否则 FALE。
GetTop(S, &e)初始条件:栈 S 已存在且非空。
操作结果:用 e 返回 S 的栈顶元素。
StackLength(S)初始条件:栈 S 已存在。
分钟)操作结果:返回 S 的元素个数,即栈的长度。
ClearStack(&S)初始条件:栈 S 已存在。
操作结果:将 S 清为空栈。
Push(&S, e)初始条件:栈 S 已存在。
操作结果:插入元素 e 为新的栈顶元素。
Pop(&S, &e)初始条件:栈 S 已存在且非空。
芯衣州星海市涌泉学校§8图§图的根本概念图:图是数据构造G =(V ,E),其中V 是结点的有穷非空集合,结点的偶对为边,E 是边的集合。
图中的结点又称为顶点。
无向图与有向图:假设图中每条边都是没有方向的,那么称为无向图;无向图中的边表示图中顶点的无序对,即〔u,v 〕和〔v,u 〕表示同一条边。
如图8.1.1V 〔G1〕={1,2,3,4,5}E 〔G1〕={(1,2),(1,4),(2,3),(2,5),(3,4),(3,5)}假设图中每条边都是有方向的,那么称其为有向图;有向图的边表示图中顶点的有序偶对;在有向图中,箭头表示边的方向。
如图8.1.2V 〔G2〕={1,2,3,4}E 〔G2〕={(1,2),(1,3),(2,4),(3,2),(4,3)}度、入度、出度:在一个有向图中,把以结点V 为终点的弧的数目称为V 的入度;把以结点V 为始点的弧的数目称为V 的出度。
入度和出度之和为该的结点的度。
如图8.1.2中,顶点V3的入度为2,出度为1,度为3。
途径、途径长度:图中从VS 到VP 的一条途径是指一串由顶点所成的连续序列VS ,Vi1,Vi2,……,Vin,VP,且其中(VP,Vi1),(Vi1,Vi2),……,(Vin,VP)都是E 上的边。
途径上所包含边的数目称为途径长度。
图8.1.2图8.1.1简单途径、简单回路:假设一条途径的顶点序列中,顶点不重复出现,那么称此途径为简单途径。
起点和终点一样的途径称为回路或者者环。
除了第一个顶点和最后一个顶点之外,其余顶点不重复出现的回路称为简单回路。
连通图、强连通图:在无向图G1中,假设从顶点Vt 到Vs 有途径,那么称Vt 和Vs 是连通的。
假设对于图中任意两个顶点都是连通的,那么称G1为连通图。
在有向图G2中,假设每一对顶点Vi,Vj ,从Vi 到Vj 和从Vj 到Vi 都存在途径,那么称G2为强连通图。
子图、生成子图:在图G =〔V ,E 〕和图G'=〔V',E'〕中,假设V V',E E',那么称图G'是图G 的子图。
信息学竞赛班数据结构专项培训教程——03栈和队列栈和队列是数据结构中重要的两种线性结构,它们分别具有后进先出(Last In First Out,LIFO)和先进先出(First In First Out,FIFO)的特点。
在信息学竞赛中,掌握栈和队列的基本概念、实现方式及应用场景是非常重要的。
本文将为大家介绍栈和队列的相关知识,并讲解一些典型的应用题。
首先,我们来介绍栈。
栈是一个只允许在一端插入和删除操作的线性表,这一端被称为栈顶。
栈的插入操作称为入栈,栈的删除操作称为出栈。
由于栈的特点是后进先出,所以最后入栈的元素将是第一个出栈的元素。
栈可以用数组和链表实现。
使用数组实现栈时,需要定义一个指针top来指示当前栈顶的位置,同时需要一个数组来存放栈元素。
入栈操作相当于在数组中将元素放入top位置,并将top指针向上移动;出栈操作相当于将top所指位置的元素移出,并将top指针向下移动。
使用链表实现栈时,需要定义一个结点结构体包含数据域和指针域,指针域指向下一个结点。
入栈操作相当于在链表头部插入一个结点,并将头指针指向新的结点;出栈操作相当于将头指针指向的结点删除,并将头指针指向下一个结点。
接下来,我们来介绍队列。
队列是一个只允许在一端插入和在另一端删除的线性表,插入操作一般称为入队,删除操作一般称为出队。
队列的特点是先进先出,即第一个入队的元素将是第一个出队的元素。
队列也可以用数组和链表实现。
使用数组实现队列时,需要定义两个指针front和rear,分别指示队列的头部和尾部位置。
入队操作相当于在rear位置插入元素,并将rear指针向后移动;出队操作相当于删除front位置的元素,并将front指针向后移动。
使用链表实现队列时,需要定义一个结点结构体包含数据域和指针域,指针域指向下一个结点。
同时需要定义头指针和尾指针,分别指示队列的头部和尾部。
入队操作相当于在尾指针位置插入结点,并将尾指针移到下一个结点;出队操作相当于删除头指针指向的结点,并将头指针移到下一个结点。
高中信息技术教案精选教案名称:高中信息技术《数据结构与算法》一、教学内容本节课的教学内容来自于高中信息技术教材第四章《数据结构与算法》的第二节。
本节主要介绍线性表、栈和队列的基本概念及其应用。
具体内容包括:1. 线性表的概念及其顺序存储结构;2. 栈的概念及其应用;3. 队列的概念及其应用;4. 线性表、栈和队列的算法实现。
二、教学目标1. 理解线性表、栈和队列的概念,掌握其顺序存储结构;2. 学会使用线性表、栈和队列解决实际问题;3. 培养学生的逻辑思维能力和编程实践能力。
三、教学难点与重点1. 线性表、栈和队列的概念及其存储结构;2. 线性表、栈和队列的算法实现;3. 线性表、栈和队列在实际问题中的应用。
四、教具与学具准备1. 教学PPT;2. 计算机及相关软件;3. 练习题及答案。
五、教学过程1. 实践情景引入:让学生思考在日常生活中遇到的需要用到数据结构与算法的问题,如购物时如何快速找到商品、编程时如何优化代码等。
2. 概念讲解:介绍线性表、栈和队列的概念及其顺序存储结构。
3. 例题讲解:通过具体的例题,讲解线性表、栈和队列的算法实现。
4. 随堂练习:让学生编写程序,实现线性表、栈和队列的相关操作。
5. 课堂讨论:让学生分享自己在编程实践中遇到的问题和解决方法。
六、板书设计板书内容主要包括线性表、栈和队列的定义、存储结构及基本操作。
七、作业设计1. 请用程序实现线性表的插入、删除和查找操作;2. 请用程序实现栈的压栈、出栈和查看栈顶元素操作;3. 请用程序实现队列的入队、出队和查看队首元素操作;4. 结合实际情况,思考线性表、栈和队列在生活中的应用。
八、课后反思及拓展延伸本节课通过实践情景引入,让学生思考日常生活中遇到的问题,激发学生的学习兴趣。
在讲解概念和例题时,注重引导学生理解和掌握线性表、栈和队列的基本操作,培养学生的编程实践能力。
课堂讨论环节,鼓励学生分享自己的学习心得,提高学生的沟通能力。
信息技术竞赛培训教程信息技术竞赛培训教程目录第二部分数据结构(一)栈(二)队列(三)链表(四)迭代与递推(五)递归(六)搜索与回溯(七)树与二叉树(八)排序算法(九)查找算法(十)图论基础知识l l 广度优先搜索l l 广度优先搜索第二部分算法和数据结构(一)栈说到学习和掌握数据结构,很容易让人想到的就是其最本的数据结构模式栈、队这一讲,我们就来谈谈“栈”。
“栈”的应用很广泛,大家在PASCAL程序设计中,常遇的一种错误就是“栈”超界,那么,“栈”为何物呢栈是只能在某一端插入和删除的特殊线性表。
用桶堆积物品,先堆进来的压在底下,随后一件一件往堆。
取走时,只能从上面一件一件取。
堆和取都在顶部进行,底部一般是不动的。
栈就是一种类似桶堆积物品的数据结构,进行删除和插入的一端称栈顶,另一堆称栈底。
插入一般称为进栈(PUSH),删除则称为退栈(POP)。
栈也称为后进先出表(LIFO表)。
一个栈可以用定长为N的数组S来表示,用一个栈指针TOP指向栈顶。
若TOP=0,表示栈空,TOPN时栈满。
进栈时TOP加1。
退栈时TOP减1。
当TOP then exit; if s[i] in [ * , / ] and symbol[p] in [ * , / ] then exit; can false; end; begin write String ; readlns; s s ; i 1; p 0; while i 9 ; t copys, j, i - j; valt, number[p], code; repeat if s[i] then {右括号处理} begin while symbol[p] do pop; decp; number[p] number[p 1]; end else begin {根据标志函数值作运算符入栈或出栈运算处理} while can do pop; push; end; inci; until i lengths or s[i - 1] ; end; write Result , number[0]; readln; end. 练习题1、读入一英文句子,单词之间用空格或逗号隔开,统计其中单词个数,并输出各个字母出现的频率。
栈和队列§3.1 栈栈(stack )是一种仅限于在称为栈顶(top )的一端进行插入和删除操作的线性表,另一端则被为栈底(bottom ).不含元素的空表称为空栈.栈的特点:后进先出(Last In First Out ),简称:LIFO.栈的表示和实现 和线性表类似,栈也有两种存储结构. (1).顺序栈顺序栈即采用的顺序存储结构来表示栈,通常采用数组来实现.采用顺序栈受数组空间的约束,有“溢出”的可能,编程前应作空间估算,若有溢出可能,应作溢出判断及相应的处理.在一个程序中,常常会出现同时使用多个栈的情形.为了不因栈上溢而产生错误中断,必须给每个栈预分一个较大的空间,但这并不容易做到,因为栈实际所用的最大空间很难估计;而且各个栈的实际使用量在使用期间是变化的,往往会有这样的情况,即其中一个栈发生上溢,而另一个栈还是空的.设想,若令多个栈共享空间,则将提高空间的使用效率,并减少发生栈上溢的可能.所以,可以采用两个栈共享空间的方法:假设在程序中需设两个栈,并共享一维数组空间.则利用“栈底位置不变”的特性,可将两个栈的栈底分别设在数组空间的两端,然后各自向中间伸展(如图),仅当两个栈的栈顶相遇时才可能发生上溢.(2).链栈采用链式存储结构的栈简称链栈.对于链栈,不含产生单个栈溢出的情况,但要记得回收结点空间(dispose (p )),否则会出现整个空间被占满,new (p )过程无法实现(即无法申请新的结点空间)的情况.【练习】 回文串识别输入一字符串,判断它是否为一回文串.所谓回文串是指去掉其中的空格与标点符号等非字母符号后,从前后两个方向读到的串相同,例如:ten animals I slam in a net. (我将十只动物装在网里) 输入:一字符串 输出:Yes 或No§3.2 队列队列(queue )是所有的插入都在一端进行,而所有的删除都在另一端进行的线性表.允许插入的一端称为队尾(rear ),允许删除的一端称为队头(front ). 队列的特点:先进先出(|First In First Out ),简称:FIFO.队列的表示和实现和栈一样,队列也有顺序存储和链式存储两种表示和实现方法.在顺序存储结构中,同样有溢出可能,即元素因队满而无法入队.对于队列来说,可以采用循环队列的技巧,仅当队头与队尾相遇时为队满.【例3.2.1】逐行打印二项展开式 (a + b )i 的系数: 杨辉三角形 (Pascal’s triangle)要求:采用队列实现!输入: n ——层数(n<50)25 输出:如上图的数字阵列 【算法思路】分析第 i 行元素与第 i +1行元素的关系目的是从前一行的数据可以计算下一行的数据 从第 i 行数据计算并存放第 i+1 行数据a 1 a 2 a 3 …… a n 出队列 出队列队头 队尾 队头 队尾 1 1 i = 11 2 1 21 5 5 1 3 1 4 6 4 1 4 1 5 10 10 5 1 5 1 6 15 20 15 6 1 6从数据结构上,可设计一个足够长的一维数组,以实现上述算法.【练习】根据上述思路分析,用队列的方法完成例3.2.1.【练习】再用二维数组,用其它方法完成例3.2.1.【练习】侦察兵队列有一个侦察班,由11人组成,其中6名是老侦察员,5名是新侦察员.一次执勤要穿越敌人的一道封锁线.根据当时的情况,队伍只能单线纵向排列,且前面第一、二人越过后,第三个人要返回报告情况,该侦察员随后编到队伍的末尾.接着第四、五人越过,第六人报告并排到末尾.依此类推.最后三人一齐顺次过去.越过封锁线后,队伍便形成老、新交替的队形.请问,穿越前队伍该怎样排?要求:采用队列实现!输出:O表示老队员,Y表示新队员【练习】倒水问题[问题描述]有三个分别装有a升水、b升水和c升水的量筒(c>b>a>0,且b与a互质), 如果c筒装满水,a与b均为空筒,三个筒相互倒水且不准把水倒往三个筒之外,一个往另一个筒倒水记为一次倒水.问是否能量出d升水(c>d>0),若能,请求出最少的倒水次数使它能倒出容量为d的水的方案.[输入格式]数据存放在当前目录下的文本文件“water.in”中.文件中以一行的形式存放四个正整数,分别a、b、c、d的值.[输出格式]答案输出到当前目录下的文本文件“water.out”中.第一次行是最少的倒水次数Q,第二起的Q行是每次例水时量简的水量,依次为a、b、c (输入与输出数据中同一行相邻两个数之间用空格区分.)[输入输出举例]water.in3 7 10 5water.out10 90 0 10 0 0 103 0 7 0 7 30 3 7 3 4 33 34 0 4 60 6 4 3 1 63 6 1 0 1 92 7 1 1 0 92 0 8 1 7 20 2 8 3 5 23 2 5(仅有第二组才是最优的一个解.)§3.3 栈的应用实例§3.3.1 中缀表达式和后缀表达式对于高级语言程序中的表达式,在编译时求解用栈来实现.任何一个表达式是由操作数(常量、常量名、变量名)、运算符(算术、关系和逻辑三种运算符)和界限符(左、右圆括号,结束符)所组成.运算符在两个操作数之间的表达式,如a+b、e*f-d等,称为中缀表达式.求值时,一般会有运算符号的优先权和结合权问题.例如:a+b*c-d,我们知道b*c要先求,但编译器只能从左到有逐一检查,检查到加号时尚无法知道是否可执行,待检查到乘号时,因乘号运算优先级比加号高,所有a+b不可以被执行,待继续基础到减号时,方可执行b*c.而后缀表达式是运算符在两个操作数之后,如ab*,也称为“逆波兰式”.后缀表达式可以顺序计算求值,所以编译程序常把中缀表达式变换成后缀表达式,然后再求值.(一)、将中缀表达式转换成后缀表达式在转换过程中为了确定计算次序,要按运算符的优先级进行,各运算符优先级如下表,优先【例3.3.1】将中缀表达式转换成后缀表达式.输入: 中缀表达式,如: B * (D-C) + A 输出: 后缀表达式,如: BDC-*A+ 【算法思想】设立一个栈来实现中缀表达式变后缀表达式.设中缀表达式在字符数组E 中,E 的末尾再加‘#’作为结束符,将转成后缀表达式,存于字符串A 中.对E 中表达式自左向右扫描,遇数直接送到A 中,若遇到运算符就考虑进栈,进栈的原则是保持栈顶的运算符优先级最高.即若欲进栈的算符是 ‘( ’或欲进栈的运算符优先级大于栈顶运算符的优先级,则将算符入栈,否则从栈中退出算符送至A 中,直至欲进栈的算符优先级大于栈顶算符的优先级,这时才将欲进栈的算符入栈.若遇到‘)’时,将栈中算符退出送入A 中,直至退出‘( ’为止.若遇表达式结束符‘#’,则依次退出栈中算符送入A中.根据上述算法,将中缀表达式B * (D-C) + A 转换成后缀表达式BDC-*A+ 的过程图3_1所示.【参考程序段】const m=100;var E , A , S : array [1..m] of char; { E 中为中缀式,A 中为后缀式,S 为栈} i , j , t : integer; procedure postexp; begini:=1; j:=1; t:=0;while E[ i ]<>’#’ do begin case E[ i ] of‘a ’.. ‘z ’, ‘A ’.. ‘Z ’ : beginA[ j ]:=E[ i ]; j:=j+1; end;‘(’ : begin扫描E 栈S 转换至AB * B* * ( B( * ( BD * ( - BD - * ( - BD C * BDC ) + BDC - + + BDC -* A BDC -*A # BDC -*A+ 图3_1t:=t+1; S[ t ]:= ‘(’;end;‘)’ : beginwhile s[ t ]< > ‘(’ do beginA[ j ]:=s[ t ]; j:=j+1; t:=t-1;end;t:=t-1;end;‘+’, ‘-’ : beginwhile (t< >0) and (s[t]< > ‘(’) do beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end;t:=t+1; s[ t ]:=E[ i ];end;‘*’, ‘/’ : beginwhile (t< >0) and (S[ t ]= ‘*’ or S[ t ]= ‘/’) do beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end; {while}t:=t+1; S[ t ]:=E[ i ];end;end; {case}i:=i+1;end; {while}while t< >0 do beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end;A[ j ]:= ‘#’;end;(二)、对后缀表达式求值计算一个后缀表达式的值,在算法上比中缀表达式要简单得多,这是因为后缀表达式有两个优点:表达式无括号,也不需考虑运算符的优先级.【算法思想】对后缀表达式求值要使用一个栈来实现.自左至右扫描后缀表达式,若遇数就入栈,若遇到运算符就从栈中退出两个数进行运算,并把计算结果入栈.如此下去,直至遇到结束符“#”为止.最后栈底元素值为所求得的结果.【练习】将一个中缀表达式转换成后缀表达式,并对后缀表达式求值.输入格式:(输入文件 bds.in)第一行:中缀表达式,运算数均为大写字母,运算符含乘方‘^’第二行开始:每行为表达式中字母,及其对应的值,输入顺序按字典排序输出格式: (输入文件 bds.out ) 第一行: 该中缀表达式所对应的后缀表达式 第二行: 该表达式的值 输入: (bds.in) 输出: (bds.out) B * (D - C) + A^C A 4 B 10 C 3 D 8B DC - * A C ^ + 114§3.3.2 地图着色问题对地图着色,可使用“四染色”定理,它是计算机科学中的著名定理之一,可以用不多于四色对地图着色,使相邻的行政区域不重色.应用这个定理的结论,利用回溯算法可对一幅给定的地图染色.作为地图四色的示例如图 3_3所示,图中01、02、03、04、05、06、07为行政区编号,1色、2色、3色、4色为各区域的颜色,称为色数. 【算法思想】从编号为01的区域开始逐一进行染色,对每个区域用色数1,2,3,4依次进行试探,并尽可能取小色数.若当前所取色数与周围已染色的区域不重色,则把该区的色数入栈,否则依次使用下一色数进行试探.若从1色至4色均与相邻的某区域发生重色,则需退栈回溯,修改栈顶区域的色数.在实现此算法时,可用一个关系矩阵R ( 1: n , 1: n )来描述各区域之间的边界关系.若第i 区与第j 区相邻(有共同边界),则R[ i , j ]的值为1,否则为0.图 3_3中所示的地图关系矩阵如图 3_4所示.为了记下每个区域当前染色结果而设立一个栈S( 1 : n ),栈的下标值表示区域号,栈中的内容是色数,如S[ 6 ] = 3表示06区域当前染色的色数是3.【参考程序段】const n=7; {地图中区域数}type adjar=array[1..n,1..n] of 0..1; ads=array[1..n] of 1..4;procedure mapcolor (var r:adjr; var s:ads); begins[ 1]:=1; {01区染1色}图 3_3 j1 0 1 1 1 1 1 02 1 0 0 0 0 1 03 1 0 0 1 1 0 04 1 0 1 0 1 1 05 1 0 1 1 0 1 06 1 1 0 1 1 0 07 0 0 0 0 0 0 0R i 图3_4i:=2; j:=1; { i 为区域号,j 为染色号} while i<n do beginwhile ( j< =4 ) and ( i<n ) do begin k:=1; { k 指示已染色区域号}while ( k<i ) and ( s[ k ]*R[ i , k ]<>j ) do begin k:=k+1; { 判断相邻区是否已染色}if k<i then j:=j+1 { 用j+1色继续试探} else begins[ i ]:= j; i:=i+1; j:=1;end; { 与相邻区不重色,染色结果进栈,继续对下一区从1色进行试探} end;if j.> 4 then begini:= i-1; j:=s[ i ]+1;end; { 变更栈顶区域的染色色数} end; end; end;按图3_37→6 → 5 → → 4 → 3 → 2 1从关系矩阵R 可以看出,当i = 6时,无论色数j =1 , 2 , 3 , 4都产生与相邻区重色问题 (因与i = 6相邻的区是1,2,4,5区,从栈S 可见这四个区色数分别1,2,3,4,四种色全部用完.6区再用四种色数之一,必然重色).因此必须变更栈顶色数,而5区当前色数为“4”,也不存在除4以外的可染色色数,则需继续退栈,变更S[ 4 ]:=4,由此S[ 5 ]:=3,然而此时仍然6区与相邻区重色,再次退栈,将S[ 3 ]改为S[ 3 ]:=3时才求得所有地区的染色解.§3.4 栈与递归§3.4.1 递归形式一般有两种——直接递归和间接递归,而栈是实现递归的重要手段. (一)、直接递归——子程序自己调用自己 【例2】 fac (n) = n! = 按上述递归定义形式写出fac(n) 函数如下: function fac(n:integer):integer;1 n=0 n ×fac (n -1) n>1 S 图3_5beginif n=0 then fac:=1 else fac:=n*fac(n-1); end;它的执行流程如下:一般来说,递归子程序在调用本身前应有条件语句控制何时进行递归,何时递归结束.这个条件语句就是递归边界.例如,fac 函数体中的“ if n=0 then fac:=1 ”.(二)、间接递归——子程序A 调用子程序B,子程序B 又调用子程序A 子程序A 和B 组合起来有两种结构形式: 1.B 是A 的局部对象 例: procedure A; procedure B; begin… A; {在B 中调用A}… end begin… B; {在A 中调用B} … end;2.A 和B 是两个地位相同的子程序例: procedure B (形式参数表): forward; {B 的完整说明在后} procedurde A; begin…B (实参表); {在A 中调用B} end;procedure B; {B 的首部缩写,形式参数表不再列出} begin …A; {在B 中调用A}3×fac (2) 2×fac (1) 1×fac (0) fac (0) = 16 A 的子过程B 的过程说明…end;【例题3】求出一个在8×8的棋盘上,放置8个不能互相捕捉的“皇后”的所有布局.显然,一个使“皇后”间不能互相捕捉的合理布局应该是——每列、每行确定有一个“皇后”,且在每条对角线上最多只有一个“皇后”.【数据结构设计】从8×8的棋盘,我们很容易想到用二维数组来存储布局.但是,进一步分析,每行有且仅有一个“皇后”,我们可以仅用一维数组来存储布局.SS[ i ] 表示第i行“皇后”所处的列.这一数据结构的改进,将大大简化编程.【算法思路】从空配置开始,在第1行至第top行为合理配置的基础上,递归调用自己而完成top+1行上的配置,直至第8行配置也是合理时,就找到一个解.因此,top=9是递归边界.在任一行上可能的配置有8种.开始时配置在第1列,以后改变时,顺次选择第2列,第3列,……,直至第8列,当8列配置全不合理或者选中某一列配置时,则退出递归过程,通过恢复步数的形式回溯,直到所有布局为止.要在 ( i , top) 配置“皇后”(S[ top ]:= i)的条件有两个:①第1至top-1行的第i 列不能有“皇后”,即S[j]≠i,1≤j≤top-1;②经 ( i , top) 的两条对角线上不能有“皇后”,即: | S[ j ]-i | ≠ | j -top |,1≤j≤top-1;【参考程序】program queen;vartotal : integer; {布局数}s : array[1..8] of 1..8; {布局}procedure search(top:integer);vari,j : integer;p : boolean; {p=true表示找到某列的配置}beginif top>8then begin {配置成功,打印布局}total:=total+1;writeln(total,' step: ');for i:=1 to 8 do write(s[i],' ');writeln;endelse beginfor i:=1 to 8 do begin {由第1行开始,顺序选择} p:=true;if top>1 then beginj:=1; {确定可否在(i,top)位置配置皇后} while (j<top) and p do beginif (s[j]=i) or (abs(s[j]-i)=abs(j-top))then p:=false;j:=j+1;end;end;if p then begin {top列配置成功}s[top]:=i;search(top+1); {递归执行top+1列上的配置} end;end;end;end;Begintotal:=0; {布局数计数器清0}search(1);End.§3.4.2 递归算法适用的场合1、数据的定义形式按递归定义如斐波那契数列的定义: f n = f n-1 + f n-2; f0 = 1 ; f1 = 2 ;【练习3】对应的递归程序为:function fib ( n : integer ) : integer;beginif n=0 then {递归边界}else {递归}end; {fib}★这类问题可转化为递推算法,递归边界作为递推的边界条件,如上例: f [0]:= 1; f [1]:= 2; {递推边界}for i:=2 to n do f [ i ]:= f [ i-1 ]+ f [ i-2 ];fib:= f [ n ];2、数据之间的关系(即数据结构)按递归定义,如树的遍历、图的搜索等;3、问题解法按递归算法实现,如回溯法、分治法等;递归算法效率较低,费时费空间,但递归算法可以使一个蕴含递归关系的程序简洁精炼.P. (相关知识见《数学基础补充§1》)【练习4】输出N元集的R排列,及其排列种数rn(输入: N R)C.【练习5】输出N元集的R组合,及其排列种数rn(输入: N R)练习3答案:① fib:=2 ② fib:=fib(n-2)+fib(n-1);。
§3 栈和队列§3.1 栈栈〔stack〕是一种仅限于在称为栈顶〔top〕的一端进行插入和删除操作的线性表,另一端那么被为栈底〔bottom〕。
不含元素的空表称为空栈。
栈的特点:后进先出〔Last In First Out〕,简称:栈的表示和实现和线性表类似,栈也有两种存储结构。
〔1〕.顺序栈顺序栈即采用的顺序存储结构来表示栈,通常采用数组来实现。
采用顺序栈受数组空间的约束,有“溢出〞的可能,编程前应作空间估算,假设有溢出可能,应作溢出判断及相应的处理。
在一个程序中,常常会出现同时使用多个栈的情形。
为了不因栈上溢而产生错误中断,必须给每个栈预分一个较大的空间,但这并不容易做到,因为栈实际所用的最大空间很难估计;而且各个栈的实际使用量在使用期间是变化的,往往会有这样的情况,即其中一个栈发生上溢,而另一个栈还是空的。
设想,假设令多个栈共享空间,那么将提高空间的使用效率,并减少发生栈上溢的可能。
所以,可以采用两个栈共享空间的方法:假设在程序中需设两个栈,并共享一维数组空间。
那么利用“栈底位置不变〞的特性,可将两个栈的栈底分别设在数组空间的两端,然后各自向中间伸展〔如图〕,仅当两个栈的栈顶相遇时才可能发生上溢。
〔2〕.链栈采用链式存储结构的栈简称链栈。
对于链栈,不含产生单个栈溢出的情况,但要记得回收结点空间〔dispose 〔p 〕〕,否那么会出现整个空间被占满,new 〔p 〕过程无法实现〔即无法申请新的结点空间〕的情况。
[练习] 回文串识别输入一字符串,判断它是否为一回文串。
所谓回文串是指去掉其中的空格与标点符号等非字母符号后,从前后两个方向读到的串相同,例如:ten animals I slam in a net. 〔我将十只动物装在网里〕输入:一字符串 输出:Yes 或No§3.2 队列队列〔queue 〕是所有的插入都在一端进行,而所有的删除都在另一端进行的线性表。
允许插入的一端称为队尾〔rear 〕,允许删除的一端称为队头〔front 〕。
队列的特点:先进先出〔|First In First Out 〕,简称:FIFO 。
队列的表示和实现和栈一样,队列也有顺序存储和链式存储两种表示和实现方法。
在顺序存储结构中,同样有溢出可能,即元素因队满而无法入队。
对于队列来说,可以采用循环队列的技巧,仅当队头与队尾相遇时为队满。
a 1 a 2 a 3 …… a n出队列出队列队头队尾队头[例3.2.1]逐行打印二项展开式 (a + b)i 的系数:杨辉三角形(Pascal’s triangle)1 1 i = 11 2 1 21 5 5 1 31 4 6 4 1 41 5 10 10 5 1 51 6 15 20 15 6 1 6要求:采用队列实现!输入: n ——层数〔n<50〕25输出:如上图的数字阵列[算法思路]分析第 i 行元素与第i+1行元素的关系目的是从前一行的数据可以计算下一行的数据从第 i 行数据计算并存放第 i+1 行数据从数据结构上,可设计一个足够长的一维数组,以实现上述算法。
[练习]根据上述思路分析,用队列的方法完成例3.2.1。
[练习]再用二维数组,用其它方法完成例3.2.1。
[练习]侦察兵队列有一个侦察班,由11人组成,其中6名是老侦察员,5名是新侦察员。
一次执勤要穿越敌人的一道封锁线。
根据当时的情况,队伍只能单线纵向排列,且前面第一、二人越过后,第三个人要返回报告情况,该侦察员随后编到队伍的末尾。
接着第四、五人越过,第六人报告并排到末尾。
依此类推。
最后三人一齐顺次过去。
越过封锁线后,队伍便形成老、新交替的队形。
请问,穿越前队伍该怎样排?要求:采用队列实现!输出:O表示老队员,Y表示新队员[练习]倒水问题[问题描述]有三个分别装有a升水、b升水和c升水的量筒〔c>b>a>0,且b与a互质〕, 如果c筒装满水,a与b均为空筒,三个筒相互倒水且不准把水倒往三个筒之外,一个往另一个筒倒水记为一次倒水。
问是否能量出d升水(c>d>0),假设能,请求出最少的倒水次数使它能倒出容量为d的水的方案。
[输入格式]数据存放在当前目录下的文本文件“water.in〞中。
文件中以一行的形式存放四个正整数,分别a、b、c、d的值。
[输出格式]答案输出到当前目录下的文本文件“water.out〞中。
第一次行是最少的倒水次数Q,第二起的Q行是每次例水时量简的水量,依次为a、b、c 〔输入与输出数据中同一行相邻两个数之间用空格区分。
〕[输入输出举例]water.in3 7 10 5water.out10 90 0 10 0 0 103 0 7 0 7 30 3 7 3 4 30 6 4 3 1 63 6 1 0 1 92 7 1 1 0 92 0 8 1 7 20 2 8 3 5 23 2 5〔仅有第二组才是最优的一个解。
〕§3.3 栈的应用实例§3.3.1 中缀表达式和后缀表达式对于高级语言程序中的表达式,在编译时求解用栈来实现。
任何一个表达式是由操作数〔常量、常量名、变量名〕、运算符〔算术、关系和逻辑三种运算符〕和界限符〔左、右圆括号,结束符〕所组成。
运算符在两个操作数之间的表达式,如a+b、e*f-d等,称为中缀表达式。
求值时,一般会有运算符号的优先权和结合权问题。
例如:a+b*c-d,我们知道b*c要先求,但编译器只能从左到有逐一检查,检查到加号时尚无法知道是否可执行,待检查到乘号时,因乘号运算优先级比加号高,所有a+b不可以被执行,待继续基础到减号时,方可执行b*c。
而后缀表达式是运算符在两个操作数之后,如ab*,也称为“逆波兰式〞。
后缀表达式可以顺序计算求值,所以编译程序常把中缀表达式变换成后缀表达式,然后再求值。
下表是中缀表达式所对应的后缀表达式:(一)、将中缀表达式转换成后缀表达式在转换过程中为了确定计算次序,要按运算符的优先级进行,各运算符优先级如下表,优先级数大的先执行。
[例3.3.1]将中缀表达式转换成后缀表达式。
输入: 中缀表达式,如: B * (D-C) + A 输出: 后缀表达式,如: BDC-*A+ [算法思想]设立一个栈来实现中缀表达式变后缀表达式。
设中缀表达式在字符数组E 中,E 的末尾再加‘#’作为结束符,将转成后缀表达式,存于字符串A 中。
对E 中表达式自左向右扫描,遇数直接送到A 中,假设遇到运算符就考虑进栈,进栈的原那么是保持栈顶的运算符优先级最高。
即假设欲进栈的算符是 ‘( ’或欲进栈的运算符优先级大于栈顶运算符的优先级,那么将算符入栈,否那么从栈中退出算符送至A 中,直至欲进栈的算符优先级大于栈顶算符的优先级,这时才将欲进栈的算符入栈。
假设遇到‘)’时,将栈中算符退出送入A 中,直至退出‘( ’为止。
假设遇表达式结束符‘#’,那么依次退出栈中算符送入A 中。
根据上述算法,将中缀表达式B * (D-C) + A 转换成后缀表达式BDC-*A+ 的过程图3_1所示。
扫描E 栈S 转换至A B * B * * ( B ( * ( B D * ( - BD - * ( - BD C * BDC )+BDC -[参考程序段]const m=100;var E , A , S : array [1..m] of char;{ E中为中缀式,A中为后缀式,S为栈}i , j , t : integer;procedure postexp;begini:=1; j:=1; t:=0;while E[ i ]<>’#’ do begincase E[ i ] of‘a’.. ‘z’, ‘A’.. ‘Z’ : beginA[ j ]:=E[ i ]; j:=j+1;end;‘(’ : begint:=t+1; S[ t ]:= ‘(’;end;‘)’ : beginwhile s[ t ]< > ‘(’ do beginA[ j ]:=s[ t ]; j:=j+1; t:=t-1;end;t:=t-1;end;‘+’, ‘-’ : beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end;t:=t+1; s[ t ]:=E[ i ];end;‘*’, ‘/’ : beginwhile (t< >0) and (S[ t ]= ‘*’or S[ t ]= ‘/’) do beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end; {while}t:=t+1; S[ t ]:=E[ i ];end;end; {case}i:=i+1;end; {while}while t< >0 do beginA[ j ]:=S[ t ]; j:=j+1; t:=t-1;end;A[ j ]:= ‘#’;end;(二)、对后缀表达式求值计算一个后缀表达式的值,在算法上比中缀表达式要简单得多,这是因为后缀表达式有两个优点:表达式无括号,也不需考虑运算符的优先级。
[算法思想]自左至右扫描后缀表达式,假设遇数就入栈,假设遇到运算符就从栈中退出两个数进行运算,并把计算结果入栈。
如此下去,直至遇到结束符“#〞为止。
最后栈底元素值为所求得的结果。
[练习]将一个中缀表达式转换成后缀表达式,并对后缀表达式求值。
输入格式:〔输入文件 bds.in〕第一行:中缀表达式,运算数均为大写字母,运算符含乘方‘^’第二行开始:每行为表达式中字母,及其对应的值,输入顺序按字典排序输出格式:〔输入文件 bds.out〕第一行:该中缀表达式所对应的后缀表达式第二行:该表达式的值输入输出举例:输入: (bds.in) 输出: (bds.out)B * (D - C) + A^CA 4B 10C 3D 8 B D C - * A C ^ + 114§3.3.2 地图着色问题对地图着色,可使用“四染色〞定理,它是计算机科学中的著名定理之一,可以用不多于四色对地图着色,使相邻的行政区域不重色。
应用这个定理的结论,利用回溯算法可对一幅给定的地图染色。
作为地05、06、07为行政区编号,1色、2色、3色、4色为各区域的颜色,称为色数。
[算法思想]从编号为01的区域开始逐一进行染色,对每个区域用色数1,2,3,4依次进行试探,并尽可能取小色数。
假设当前所取色数与周围已染色的区域不重色,那么把该区的色数入栈,否那么依次使用下一色数进行试探。
假设从1色至4色均与相邻的某区域发生重色,那么需退栈回溯,修改栈顶区域的色数。
在实现此算法时,可用一个关系矩阵R ( 1: n , 1: n )来描述各区域之间的边界关系。