广东省汕头市金山中学高中信息技术信息学竞赛班数据结构专项培训教程03栈和队列
- 格式:doc
- 大小:117.00 KB
- 文档页数:12
集合与记录类型§6.1 集合类型§6.1.1 集合类型的定义集合是同类型对象的一个汇集,它是指同类型对象汇集在一起构成的数据结构。
集合的每一个对象称为集合的元素。
集合元素必须是有序简单数据类型,集合元素的类型称为集合的基类型。
集合的一般形式为:TYPE <类型标识符> = set of <基类型>;基类型可以是整型、字符型、布尔型、枚举型、子界型等,但不能是实型或结构类型。
例如:TYPE letter = set of ‘A’.. ‘Z’;var ch1, ch2 : letter;也可以直接写成:var ch1, ch2 : set of ‘A’.. ‘Z’;在Pascal中集合是用一组括在方括号中的元素来表示,元素之间用逗号分隔。
如:[A, B , C , D] 是四个枚举量的集合[ 1 .. 20 ] 表示1到20的所有整数的集合[ ‘0’ ] 是单元素集[ ] 表示空集一个集合类型变量的取值,可以是基类型中所有元素按不同的组合而构成的子集。
例如,上面说明变量ch1的类型是letter,它可以是下列的组合:[‘A’ .. ‘Z’] 全集[‘A’, ‘B’, ‘Q’] 任一子集[‘A’ .. ‘C’ , ‘X’ ..‘Z’ ][‘A’ ] 单元素集[ ] 空集空集与所有的集合类型都兼容。
§6.1.2 集合类型的运算ch1:=[ [‘A’ .. ‘C’]];是合法的集合赋值。
对集合除可以进行赋值运算外,还可以进行以下运算:·交(*)运算:两集合之交 S1*S2 为一集合,所得元素由S1、S2中相同的元素组成。
如: [ 0..7 ] * [ 0..4 ] = [ 0..4 ]·并(+)运算:两集合之并 S1+S2 为一集合,所得元素由S1、S2中所有相同的元素组成。
如: [ 0..7 ] + [ 0..4 ] = [ 0..7 ][ 0 , 1 ] + [ 1 , 4 , 6 ] = [ 0 , 1 , 4 , 6 ]·差(-)运算:两集合之差 S1-S2 为一集合,所得元素由只存在于S1而不在S2的那些元素组成。
数组【引例】输入20个数,将它们按从大到小的次序排序后输出。
讨论:如果按我们前面学的知识,我们应设20个变量来存储这20个数,如果要排序的数不是20个,而是100个,那我们就应设100个变量?没这么笨吧,我们有更好的办法解决。
§5.1 一维数组数组是由固定数量....的相同类型....的元素按一定顺序排列而成。
只有一个下标类型的数组称为一维数组。
1.数组类型定义和说明类型定义的一般形式为:TYPE <类型标识符> = ARRAY [下标类型] OF <基类型>;数组说明:VAR <数组名> :<数组类型标识符>;数组名是由用户定义的标识符,下标类型可以是子界类型或枚举类型,下标规定了数组元素的个数和排列次序。
基类型表示数组中每个元素的类型,它可以是任何数据类型,但同一数组中的元素类型必须相同。
如:typeA = array [1..20] of integer;B = array [0..50] of char;Varx , y : A;a : B;其中x 、y 被说明为A 类型数据,即均为拥有20个元素的数组,下标从1到20,元素类型为整型;a 被说明为B 类型数据,即拥有51个元素的数组,下标从0到50,元素类型为字符型。
★ 数组也可以直接在说明部分说明数组的类型,如:var x , y : array [1..20] of integer;a : array [0..50] of char;数组中的每个元素都是变量,每个元素在数组中有固定的位置,可以用数组名及方括号括起的下标..来表示。
如a 数组中的第5个元素可表示为:a [4]数组元素的运算和变量相同,如:readln (a[4]); x[3]:=x[3]+y[1];2.数组元素的赋值和引用如为一个数组A[1..10] 赋值,可用下列语句实现:for i:=1 to 10 do read (A[i]);如果两个数组类型相同,如数组x 和y ,可用赋值语句: x:=y ;把y 的10个元素值赋给x 的相应元素,它等效于: for i:=1 to 20 do x[i]:=y[i]; 但要给数组元素赋同一个值,不能这样赋值:x:=0;0 1 2 3 4 5 6 7 …… 50 a 下标而应该用如下语句:for i:=1 to 20 do x[i]:=0;【例1】求100以内的所有素数。
§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 )。
§9 内部排序在《数据结构》里,排序一般分为:插入排序、交换排序、选择排序、归并排序和基数排序五种。
写在前面的话:在看下面的各种算法之前,请先想想,如果给你一个无序的数列,你如何去排序?设计出你自己的算法。
还有没有其它方法?相信自己的能力,排序算法是连小学生都可以设计出的!不希望以后听到这样的话:“排序的算法我忘了……”,排序算法不是背出来的!§9.1 插入排序(Insertion Sort)基本思想:每次将一个待排序的数据元素,插入到前面已经排好序的数列中的适当位置,使数列依然有序;直到待排序数据元素全部插入完为止。
排序过程:【示例】: [初始关键字] [49] 38 65 97 76 13 27 49J=2(38) [38 49] 65 97 76 13 27 49J=3(65) [38 49 65] 97 76 13 27 49J=4(97) [38 49 65 97] 76 13 27 49J=5(76) [38 49 65 76 97] 13 27 49J=6(13) [13 38 49 65 76 97] 27 49J=7(27) [13 27 38 49 65 76 97] 49J=8(49) [13 27 38 49 49 65 76 97]§9.2选择排序基本思想:每一趟从待排序的数据元素中选出最小(或最大)的一个元素,顺序放在已排好序的数列的最后,直到全部待排序的数据元素排完。
排序过程:【示例】:初始关键字 [49 38 65 97 76 13 27 49]第一趟排序后 13 [38 65 97 76 49 27 49]第二趟排序后 13 27 [65 97 76 49 38 49]第三趟排序后 13 27 38 [97 76 49 65 49]第四趟排序后 13 27 38 49 [49 97 65 76]第五趟排序后 13 27 38 49 49 [97 97 76]第六趟排序后 13 27 38 49 49 76 [76 97]第七趟排序后 13 27 38 49 49 76 76 [97]最后排序结果 13 27 38 49 49 76 76 97§9.3 冒泡排序 (BubbleSort)基本思想:两两比较待排序数据元素的大小,发现两个数据元素的次序相反时即进行交换,直到没有反序的数据元素为止。
§3栈和队列§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 )。
信息学竞赛班数据结构专项培训教程——03栈和队列栈和队列是数据结构中重要的两种线性结构,它们分别具有后进先出(Last In First Out,LIFO)和先进先出(First In First Out,FIFO)的特点。
在信息学竞赛中,掌握栈和队列的基本概念、实现方式及应用场景是非常重要的。
本文将为大家介绍栈和队列的相关知识,并讲解一些典型的应用题。
首先,我们来介绍栈。
栈是一个只允许在一端插入和删除操作的线性表,这一端被称为栈顶。
栈的插入操作称为入栈,栈的删除操作称为出栈。
由于栈的特点是后进先出,所以最后入栈的元素将是第一个出栈的元素。
栈可以用数组和链表实现。
使用数组实现栈时,需要定义一个指针top来指示当前栈顶的位置,同时需要一个数组来存放栈元素。
入栈操作相当于在数组中将元素放入top位置,并将top指针向上移动;出栈操作相当于将top所指位置的元素移出,并将top指针向下移动。
使用链表实现栈时,需要定义一个结点结构体包含数据域和指针域,指针域指向下一个结点。
入栈操作相当于在链表头部插入一个结点,并将头指针指向新的结点;出栈操作相当于将头指针指向的结点删除,并将头指针指向下一个结点。
接下来,我们来介绍队列。
队列是一个只允许在一端插入和在另一端删除的线性表,插入操作一般称为入队,删除操作一般称为出队。
队列的特点是先进先出,即第一个入队的元素将是第一个出队的元素。
队列也可以用数组和链表实现。
使用数组实现队列时,需要定义两个指针front和rear,分别指示队列的头部和尾部位置。
入队操作相当于在rear位置插入元素,并将rear指针向后移动;出队操作相当于删除front位置的元素,并将front指针向后移动。
使用链表实现队列时,需要定义一个结点结构体包含数据域和指针域,指针域指向下一个结点。
同时需要定义头指针和尾指针,分别指示队列的头部和尾部。
入队操作相当于在尾指针位置插入结点,并将尾指针移到下一个结点;出队操作相当于删除头指针指向的结点,并将头指针移到下一个结点。
栈和队列§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*,也称为“逆波兰式”。
后缀表达式可以顺序计算求值,所以编译程序常把中缀表达式变换成后缀表达式,然后再求值。
(一)、将中缀表达式转换成后缀表达式在转换过程中为了确定计算次序,要按运算符的优先级进行,各运算符优先级如下表,优先级数大的先执行。
输入:中缀表达式,如: 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; 扫描E 栈S 转换至AB * B* * ( B( * ( BD * ( -BD- * ( -BDC * BDC) + BDC-+ + BDC-*A BDC-*A# BDC-*A+图3_1begini:=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;‘+’, ‘-’ : 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^CA 4B 10C 3D 8 B D C - * 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色均与相邻的某区域发生重色,则需退栈回溯,修改栈顶区域的色数。