数据结构习题解析第4章.docx
《数据结构习题解析第4章.docx》由会员分享,可在线阅读,更多相关《数据结构习题解析第4章.docx(38页珍藏版)》请在冰豆网上搜索。
![数据结构习题解析第4章.docx](https://file1.bdocx.com/fileroot1/2023-1/28/15885b87-f909-4013-88ce-5fcc9ee9acb3/15885b87-f909-4013-88ce-5fcc9ee9acb31.gif)
数据结构习题解析第4章
第4章栈和队列
一、复习要点
本章主要讨论3种线性结构:
栈、队列与优先级队列。
这3种结构都是顺序存取的表,而且都是限制存取点的表。
栈限定只能在表的一端(栈顶)插入与删除,其特点是先进后出。
队列和优先级队列限定只能在表的一端(队尾)插入在另一端(队头)删除,不过优先级队列在插入和删除时需要根据数据对象的优先级做适当的调整,令优先级最高的对象调整到队头,其特点是优先级高的先出。
而队列不调整,其特点是先进先出。
这几种结构在开发各种软件时非常有用。
本章复习的要点:
1、基本知识点
要求理解栈的定义和特点,栈的抽象数据类型和在递归和表达式计算中的使用,在栈式铁路调车线上当进栈序列为1,2,3,⋯,n时,可能的出栈序列计数,栈的顺序存储表示和链接存储表示,特别要注意,链式栈的栈顶应在链头,插入与删除都在链头进行。
另外,需要理解队列的定义和特点,队列的抽象数据类型和在分层处理中的使用,队列的顺序存储表示(循环队列)和链接存储表示,需要注意的是,链式队列的队头应在链头,队尾应在链尾。
还需要理解优先级队列的定义和特点。
优先级队列的最佳存储表示是堆(heap),本章介绍的表示看懂即可。
2、算法设计
Ø栈的5种操作(进栈、退栈、取栈顶元素、判栈空、置空栈)的在顺序存储表示下的实现,以及在链接存储表示下的实现。
Ø使用栈的后缀表达式计算算法
Ø双栈共用一个数组的进栈、退栈、置空栈、判栈空算法及栈满、栈空条件
Ø使用两个栈模拟一个队列时的进队列和出队列算法
Ø循环队列的进队列、出队列、取队头元素、判队列空、置空队列操作的实现
Ø使用tag区分队列空和队列满的循环队列的进队列和出队列操作的实现
Ø链式队列的进队列、出队列、取队头元素、判队列空、置空队列操作的实现
Ø使用队尾指针rear和队列长度length的链式队列的进队列、出队列、取队头元素、判队列空、置空队列操作的实现
Ø队列在分层处理中的使用事例(杨辉三角形按层次打印)
Ø双端队列的顺序存储表示及其进队列、出队列算法及队空、队满条件
二、难点和重点
1、栈:
栈的特性、栈的基本运算
Ø栈的数组实现、栈的链表实现
Ø栈满及栈空条件、抽象数据类型中的先决条件与后置条件
2、栈的应用:
用后缀表示计算表达式,中缀表示改后缀表示
3、队列:
队列的特性、队列的基本运算
Ø队列的数组实现:
循环队列中队头与队尾指针的表示,队满及队空条件
Ø队列的链表实现:
链式队列中的队头与队尾指针的表示、
4、双向队列:
双向队列的插入与删除算法
5、优先级队列:
优先级队列的插入与删除算法
三、教材中习题的解析
4-1改写顺序栈的进栈成员函数Push(x),要求当栈满时执行一个stackFull()操作进行栈满处理。
其功能是:
动态创建一个比原来的栈数组大二倍的新数组,代替原来的栈数组,原来栈数组中的元素占据新数组的前MaxSize位置。
【解答】
templatevoidstack:
:
push(constType&item){
if(isFull())stackFull();//栈满,做溢出处理
elements[++top]=item;//进栈
}
templatevoidstack:
:
stackFull(){
Type*temp=newType[3*maxSize];//创建体积大二倍的数组
for(inti=0;i<=top;i++)temp[i]=elements[i];//传送原数组的数据
delete[]elements;//删去原数组
maxSize*=3;//数组最大体积增长二倍
elements=temp;//新数组成为栈的数组空间
}
4-2铁路进行列车调度时,常把站台设计成栈式结构的站台,如右图所示。
试问:
(1)设有编号为1,2,3,4,5,6的六辆列车,顺序开入栈式结构的站台,则可能的出栈序列有多少种?
(2)若进站的六辆列车顺序如上所述,那么是否能够得到435612,325641,154623和135426的出站序列,如果不能,说明为什么不能;如果能,说明如何得到(即写出"进栈"或"出栈"的序列)。
【解答】
(1)可能的不同出栈序列有种。
(2)不能得到435612和154623这样的出栈序列。
因为若在4,3,5,6之后再将1,2出栈,则1,2必须一直在栈中,此时1先进栈,2后进栈,2应压在1上面,不可能1先于2出栈。
154623也是这种情况。
出栈序列325641和135426可以得到。
3
5
6
2
2
4
4
4
4
1
1
1
1
1
1
1
1
33232325325325632564325641
5
3
4
4
1
2
2
2
2
6
1113135********213542135426
4-3试证明:
若借助栈可由输入序列1,2,3,…,n得到一个输出序列p1,p2,p3,…,pn(它是输入序列的某一种排列),则在输出序列中不可能出现以下情况,即存在i(提示:
用反证法)
【解答】
充分性:
由j下面详细解释一下。
借助栈由输入序列1,2,3,…,n,可得到输出序列p1,p2,p3,…,pn,如果存在下标i,j,k,满足i①i进栈,i出栈,j进栈,j出栈,k进栈,k出栈。
此时具有最小值的排在最前面pi位置,具有中间值的排在其后pj位置,具有最大值的排在pk位置,有pi②i进栈,i出栈,j进栈,k进栈,k出栈,j出栈。
此时具有最小值的排在最前面pi位置,具有最大值的排在pj位置,具有中间值的排在最后pk位置,有pi③i进栈,j进栈,j出栈,i出栈,k进栈,k出栈。
此时具有中间值的排在最前面pi位置,具有最小值的排在其后pj位置,有pj④i进栈,j进栈,j出栈,k进栈,k出栈,i出栈。
此时具有中间值的排在最前面pi位置,具有最大值的排在其后pj位置,具有最小值的排在pk位置,有pk⑤i进栈,j进栈,k进栈,k出栈,j出栈,i出栈。
此时具有最大值的排在最前面pi位置,具有中间值的排在其后pj位置,具有最小值的排在pk位置,有pk4-4将编号为0和1的两个栈存放于一个数组空间V[m]中,栈底分别处于数组的两端。
当第0号栈的栈顶指针top[0]等于-1时该栈为空,当第1号栈的栈顶指针top[1]等于m时该栈为空。
两个栈均从两端向中间增长。
当向第0号栈插入一个新元素时,使top[0]增1得到新的栈顶位置,当向第1号栈插入一个新元素时,使top[1]减1得到新的栈顶位置。
当top[0]+1==top[1]时或top[0]==top[1]-1时,栈空间满,此时不能再向任一栈加入新的元素。
试定义这种双栈(DoubleStack)结构的类定义,并实现判栈空、判栈满、插入、删除算法。
【解答】
双栈的类定义如下:
#include
templateclassDblStack{//双栈的类定义
private:
inttop[2],bot[2];//双栈的栈顶指针和栈底指针
Type*elements;//栈数组
intm;//栈最大可容纳元素个数
public:
DblStack(intsz=10);//初始化双栈,总体积m的默认值为10
~DblStack(){delete[]elements;}//析构函数
voidDblPush(constType&x,inti);//把x插入到栈i的栈顶
intDblPop(inti);//退掉位于栈i栈顶的元素
Type*DblGetTop(inti);//返回栈i栈顶元素的值
intIsEmpty(inti)const{returntop[i]==bot[i];}//判栈i空否,空返回1,否则返回0
intIsFull()const{returntop[0]+1==top[1];}//判栈满否,满则返回1,否则返回0
voidMakeEmpty(inti);//清空栈i的内容
}
templateDblStack:
:
DblStack(intsz):
m(sz),top[0](-1),bot[0](-1),top[1](sz),bot[1](sz){
//建立一个最大尺寸为sz的空栈,若分配不成功则错误处理。
elements=newType[m];//创建栈的数组空间
assert(elements!
=NULL);//断言:
动态存储分配成功与否
}
templatevoidDblStack:
:
DblPush(constType&x,inti){
//如果IsFull(),则报错;否则把x插入到栈i的栈顶
assert(!
IsFull());//断言:
栈满则出错处理,停止执行
if(i==0)elements[++top[0]]=x;//栈0情形:
栈顶指针先加1,然后按此地址进栈
elseelements[--top[1]]=x;//栈1情形:
栈顶指针先减1,然后按此地址进栈
}
templateintDblStack:
:
DblPop(inti){
//如果IsEmpty(i),则不执行退栈,返回0;否则退掉位于栈i栈顶的元素,返回1
if(IsEmpty(i))return0;//判栈空否,若栈空则函数返回0
if(i==0)top[0]--;//栈0情形:
栈顶指针减1
elsetop[1]++;//栈1情形:
栈顶指针加1
return1;
}
templateType*DblStack:
:
DblGetTop(inti){
//若栈不空则函数返回该栈栈顶元素的地址。
if(IsEmpty(inti))returnNULL;//判栈i空否,若栈空则函数返回空指针
return&elements[top[i]];//返回栈顶元素的值
}
templatevoidMakeEmpty(inti){
if(i==0)top[0]=bot[0]=-1;
elsetop[1]=bot[1]=m;
}
4-5写出下列中缀表达式的后缀形式:
(1)A*B*C
(2)-A+B-C+D
(3)A*-B+C
(4)(A+B)*D+E/(F+A*D)+C
(5)A&&B||!
(E>F)/*注:
按C++的优先级*/
(6)!
(A&&!
((BD)))||(C【解答】
(1)AB*C*
(2)A-B+C-D+
(3)AB-*C+
(4)AB+D*EFAD*+/+C+
(5)AB&&EF>!
||
(6)ABC||!
&&!
CE<||
4-6根据课文中给出的优先级,回答以下问题:
(1)在函数postfix中,如果表达式e含有n个运算符和分界符,问栈中最多可存入多少个元素?
(2)如果表达式e含有n个运算符,且括号嵌套的最大深度为6层,问栈中最多可存入多少个元素?
【解答】
(1)在函数postfix中,如果表达式e含有n个运算符和分界符,则可能的运算对象有n+1个。
因此在利用后缀表达式求值时所用到的运算对象栈中最多可存入n+1个元素。
(2)同上。
4-7设表达式的中缀表示为a*x-b/x↑2,试利用栈将它改为后缀表示ax*bx2↑/-。
写出转换过程中栈的变化。
【解答】
若设当前扫描到的运算符ch的优先级为icp(ch),该运算符进栈后的优先级为isp(ch),则可规定各个算术运算符的优先级如下表所示。
运算符
;
(
^
*,/,%
+,-
)
isp
0
1
7
5
3
8
icp
0
8
6
4
2
1
isp也叫做栈内(instackpriority)优先数,icp也叫做栈外(incomingpriority)优先数。
当刚扫描到的运算符ch的icp(ch)大于isp(stack)时,则ch进栈;当刚扫描到的运算符ch的icp(ch)小于isp(stack)时,则位于栈顶的运算符退栈并输出。
从表中可知,icp(“(”)最高,但当“(”进栈后,isp(“(”)变得极低。
其它运算符进入栈中后优先数都升1,这样可体现在中缀表达式中相同优先级的运算符自左向右计算的要求。
运算符优先数相等的情况只出现在括号配对“)”或栈底的“;”号与输入流最后的“;”号配对时。
前者将连续退出位于栈顶的运算符,直到遇到“(”为止。
然后将“(”退栈以对消括号,后者将结束算法。
步序
扫描项
项类型
动作
栈的变化
输出
0
☞';'进栈,读下一符号
;
1
a
操作数
☞直接输出,读下一符号
;
A
2
*
操作符
☞isp(';');*
A
3
x
操作数
☞直接输出,读下一符号
;*
ax
4
-
操作符
☞isp('*')>icp('-'),退栈输出
;
ax*
☞isp(';');-
ax*
5
b
操作数
☞直接输出,读下一符号
;-
ax*b
6
/
操作符
☞isp('-');-/
ax*b
7
x
操作数
☞直接输出,读下一符号
;-/
ax*bx
8
↑
操作符
☞isp('/');-/↑
ax*bx
9
2
操作数
☞直接输出,读下一符号
;-/↑
ax*bx2
10
;
操作符
☞isp('↑')>icp(';'),退栈输出
;-/
ax*bx2↑
☞isp('/')>icp(';'),退栈输出
;-
ax*bx2↑/
☞isp('-')>icp(';'),退栈输出
;
ax*bx2↑/-
☞结束
4-8试利用运算符优先数法,画出对如下中缀算术表达式求值时运算符栈和运算对象栈的变化。
a+b*(c-d)-e↑f/g
【解答】
设在表达式计算时各运算符的优先规则如上一题所示。
因为直接对中缀算术表达式求值时必须使用两个栈,分别对运算符和运算对象进行处理,假设命名运算符栈为OPTR(operator的缩写),运算对象栈为OPND(operand的缩写),下面给出对中缀表达式求值的一般规则:
(1)建立并初始化OPTR栈和OPND栈,然后在OPTR栈中压入一个“;”
(2)从头扫描中缀表达式,取一字符送入ch。
(3)当ch不等于“;”时,执行以下工作,否则结束算法。
此时在OPND栈的栈顶得到运算结果。
①如果ch是运算对象,进OPND栈,从中缀表达式取下一字符送入ch;
②如果ch是运算符,比较ch的优先级icp(ch)和OPTR栈顶运算符isp(OPTR)的优先级:
☞若icp(ch)>isp(OPTR),则ch进OPTR栈,从中缀表达式取下一字符送入ch;
☞若icp(ch)☞若icp(ch)==isp(OPTR)且ch==“)”,则从OPTR栈退出栈顶的“(”,对消括号,然后从中缀表达式取下一字符送入ch;
根据以上规则,给出计算a+b*(c-d)-e↑f/g时两个栈的变化。
步序
扫描项
项类型
动作
OPND栈
OPTR栈
0
☞OPTR栈与OPND栈初始化,‘;’进OPTR栈,
取第一个符号
;
1
a
操作数
☞a进OPND栈,取下一符号
a
;
2
+
操作符
☞icp(‘+’)>isp(‘;’),进OPTR栈,取下一符号
a
;+
3
b
操作数
☞b进OPND栈,取下一符号
ab
;+
4
*
操作符
☞icp(‘*’)>isp(‘+’),进OPTR栈,取下一符号
ab
;+*
5
(
操作符
☞icp(‘(’)>isp(‘*’),进OPTR栈,取下一符号
ab
;+*(
6
c
操作数
☞c进OPND栈,取下一符号
abc
;+*(
7
-
操作符
☞icp(‘-’)>isp(‘(’),进OPTR栈,取下一符号
ab
;+*(-
8
d
操作数
☞d进OPND栈,取下一符号
abcd
;+*(-
9
)
操作符
☞icp(‘)’)栈‘c’,退OPTR栈‘-’,计算c–d→s1,结果
进OPND栈
abs1
;+*(
10
同上
同上
☞icp(‘)’)==isp(‘(’),退OPTR栈‘(’,对消括号,
取下一符号
abs1
;+*
11
-
操作符
☞icp(‘-’)栈‘b’,退OPTR栈‘*’,计算b*s1→s2,结果
进OPND栈
as2
;+
12
同上
同上
☞icp(‘-’)栈‘a’,退OPTR栈‘+’,计算a*s2→s3,结果
进OPND栈
s3
;
13
同上
同上
☞icp(‘-’)>isp(‘;’),进OPTR栈,取下一符号
s3
;-
14
e
操作数
☞e进OPND栈,取下一符号
s3e
;-
15
↑
操作符
☞icp(‘↑’)>isp(‘-’),进OPTR栈,取下一符号
s3e
;-↑
16
f
操作数
☞f进OPND栈,取下一符号
s3ef
;-↑
17
/
操作符
☞icp(‘/’)栈‘e’,退OPTR栈‘↑’,计算e↑f→s4,结果
进OPND栈
s3s4
;-
18
同上
同上
☞icp(‘/’)>isp(‘-’),进OPTR栈,取下一符号
s3s4
;-/
19
g
操作数
☞g进OPND栈,取下一符号
s3s4g
;-/
20
;
操作符
☞icp(‘;’)栈‘s4’,退OPTR栈‘/’,计算s4/g→s5,结果
进OPND栈
s3s5
;-
21
同上
同上
☞icp(‘;’)栈‘s3’,退OPTR栈‘-’,计算s3–s5→s6,结
果进OPND栈
s6
;
22
同上
同上
☞icp(‘;’)==