第4章栈与队列 第4章栈和队列 4-1改写顺序栈的进栈成员函数Push(x),要求当栈满时执行一个 stackFull()操作进行栈满处理。其功能 是:动态创建一个比原来的栈数组大二倍的新数组,代替原来的栈数组,原来栈数组中的元素占据新数组 的前 Max size位置。 【解答】 template void stack:push( const Type&iem){ if( isFull ()stack 栈满,做溢出处理 elements [++top I ∥进栈 mplate void stack: stack Full ()i Type*temp= new Type[3* maxS ize;∥建体积大二倍的数组 for( int i=0; i<= top; 1++) ∥传送原数组的数据 temp elements 0; delete elements ∥删去原数组 maxS ize *=3 ∥数组最大体积增长二倍 elements =temp ∥新数组成为栈的数组空间 4-2铁路进行列车调度时,常把站台设计成栈式结构的站台,如右图所示。试问 (1)设有编号为1,2,3,4,5,6的六辆列车,顺序开入栈式结构的站台,则可能 的出栈序列有多少种? 234 (2)若进站的六辆列车顺序如上所述,那么是否能够得到435612,325641, 154623和135426的出站序列,如果不能,说明为什么不能;如果能,说明如何 得到即写出"进栈"或"出栈"的序列) 【解答】 (1)可能的不同出栈序列有(1/(6+1)*C12=132种 (2)不能得到435612和154623这样的出栈序列。因为若在4,3,5,6之后再将1,2出栈,则1,2必须 直在栈中,此时1先进栈,2后进栈,2应压在1上面,不可能1先于2出栈。154623也是这种情况。 出栈序列325641和135426可以得到。 32 32 325 3256 2564 325641 13 13513541354213542135426 4-3试证明:若借助栈可由输入序列1,2,3,…,n得到一个输出序列pi,p2,p,…,pn(它是输入序列的某 种排列),则在输出序列中不可能出现以下情况,即存在i<j<k,使得p<pk<p。(提示:用反证法)
第 4 章 栈与队列 36 (1/(6 1)) 132 6 + C12 = 第 4 章 栈和队列 4-1 改写顺序栈的进栈成员函数 Push (x ),要求当栈满时执行一个 stackFull ( )操作进行栈满处理。其功能 是:动态创建一个比原来的栈数组大二倍的新数组,代替原来的栈数组,原来栈数组中的元素占据新数组 的前 MaxSize 位置。 【解答】templatevoid stack :: push ( const Type & item ) { if ( isFull ( ) ) stackFull ( ); //栈满,做溢出处理 elements [ ++top ] = item; //进栈 } template void stack :: stackFull ( ) { Type * temp = new Type [ 3 * maxSize ]; //创建体积大二倍的数组 for ( int i = 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 3 32 32 325 325 3256 32564 325641 5 3 4 4 1 2 2 2 2 6 1 1 13 135 1354 13542 13542 135426 4-3 试证明:若借助栈可由输入序列 1, 2, 3, …, n 得到一个输出序列 p1, p2, p3, …, pn (它是输入序列的某一 种排列),则在输出序列中不可能出现以下情况,即存在 i < j < k,使得 pj < pk < pi。(提示:用反证法)
第4章栈与队列 【解答】 因为借助栈由输入序列1,2,3,…n,可得到输出序列pl,p,p,…,pa,如果存在下标i,jk,满足i DblStack DblStack( int sz ): m(sz), top[o](-1), bot[o(-1), top[1I(sz), bot(l](sz)i ∥建立一个最大尺寸为sz的空栈,若分配不成功则错误处理
第 4 章 栈与队列 37 【解答】 因为借助栈由输入序列 1, 2, 3, …, n,可得到输出序列 p1, p2, p3, …, pn ,如果存在下标 i, j, k,满足 i template class DblStack { //双栈的类定义 private: int top[2], bot[2]; //双栈的栈顶指针和栈底指针 Type *elements; //栈数组 int m; //栈最大可容纳元素个数 public: DblStack ( int sz =10 ); //初始化双栈, 总体积 m 的默认值为 10 ~DblStack ( ) { delete [ ] elements; } //析构函数 void DblPush ( const Type& x, int i ); //把 x 插入到栈 i 的栈顶 int DblPop ( int i ); //退掉位于栈 i 栈顶的元素 Type * DblGetTop ( int i ); //返回栈 i 栈顶元素的值 int IsEmpty ( int i ) const { return top[i] == bot[i]; } //判栈 i 空否, 空则返回 1, 否则返回 0 int IsFull ( ) const { return top[0]+1 == top[1]; } //判栈满否, 满则返回 1, 否则返回 0 void MakeEmpty ( int i ); //清空栈 i 的内容 } template DblStack :: DblStack ( int sz ) : m(sz), top[0] (-1), bot[0](-1), top[1](sz), bot[1](sz) { //建立一个最大尺寸为 sz 的空栈, 若分配不成功则错误处理。 0 m-1 bot[0] top[0] top[1] bot[1]
第4章栈与队列 elements = new Type[m] ∥创建栈的数组空间 assert( elements =NULL 断言:动态存储分配成功与否 template void DblStack:: DblPush( const Type& x, int i)i 如果 IsFull(),则报错:否则把x插入到栈i的栈顶 ssert(!sFull () ∥断言:栈满则出错处理,停止执行 if(i==0)elements[ ++top(O]=x 栈0情形:栈顶指针先加1,然后按此地址进栈 else elements[--top[=x; ∥栈1情形:栈顶指针先减1,然后按此地址进栈 template int DbIStack : DblPop( int i)i 如果 IsEmpty(i),则不执行退栈,返回0:否则退掉位于栈i栈顶的元素,返回1 if( IsEmpty(i))return 0; ∥判栈空否,若栈空则函数返回0 if(i==0)top[o]-; ∥栈0情形:栈顶指针减1 lse top[ 1]++; 俄栈1情形:栈顶指针加1 return I template Type* DblsStack DblGetTop( int i)( ∥若栈不空则函数返回该栈栈顶元素的地址 if IsEmpty int i))return NULL ∥判栈i空否,若栈空则函数返回空指针 return& elements[ top ∥返回栈顶元素的值 template void Make Empty int i)& if(i=0)top[0]=bot[0]=-1; else top[ 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! (6)ABC‖|!&&!CE<
第 4 章 栈与队列 38 elements = new Type[m]; //创建栈的数组空间 assert ( elements != NULL ); //断言: 动态存储分配成功与否 } template void DblStack :: DblPush ( const Type& x, int i ) { //如果 IsFull ( ),则报错;否则把 x 插入到栈 i 的栈顶 assert ( !IsFull ( ) ); //断言: 栈满则出错处理,停止执行 if ( i == 0 ) elements[ ++top[0] ] = x; //栈 0 情形:栈顶指针先加 1, 然后按此地址进栈 else elements[--top[1] ] = x; //栈 1 情形:栈顶指针先减 1, 然后按此地址进栈 } template int DblStack :: DblPop ( int i ) { //如果 IsEmpty ( i ),则不执行退栈,返回 0;否则退掉位于栈 i 栈顶的元素,返回 1 if ( IsEmpty ( i ) ) return 0; //判栈空否, 若栈空则函数返回 0 if ( i == 0 ) top[0]--; //栈 0 情形:栈顶指针减 1 else top[1]++; //栈 1 情形:栈顶指针加 1 return 1; } template Type * DblStack :: DblGetTop ( int i ) { //若栈不空则函数返回该栈栈顶元素的地址。 if ( IsEmpty ( int i ) ) return NULL; //判栈 i 空否, 若栈空则函数返回空指针 return& elements[ top[i] ]; //返回栈顶元素的值 } template void MakeEmpty ( int i ) { if ( i == 0 ) top[0] = bot[0] = -1; else top[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 && !( (B D) ) )||(C ! || (6) A B C || ! && ! C E < ||
第4章栈与队列 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也叫做栈内( (in stack priority)优先数,icp也叫做栈外( in coming priority)优先数。当刚扫描到的 运算符ch的icp(ch)大于isp( stack)时,则ch进栈;当刚扫描到的运算符ch的icp(ch)小于isp( stack)时 则位于栈顶的运算符退栈并输出。从表中可知,icp(“()最高,但当“(”进栈后,isp(“()变得极低。其它 运算符进入栈中后优先数都升1,这样可体现在中缀表达式中相同优先级的运算符自左向右计算的要求 运算符优先数相等的情况只出现在括号配对“)”或栈底的“;”号与输入流最后的“;”号配对时。前者将 连续退出位于栈顶的运算符,直到遇到“(”为止。然后将“(”退栈以对消括号,后者将结束算法 步序扫描项项类型 栈的变化 操作数直接输出、读下一符号 操作符「isp(;')icp(-'),退栈输出 isp(";")icp(;"),退栈输出 ax*bx2↑ isp("/")>icp(';"),退栈输出 ax*bx2↑/ isp("-')>ip(';"),退栈输出 axbx2↑/ 4-8试利用运算符优先数法,画出对如下中缀算术表达式求值时运算符栈和运算对象栈的变化 a+b*(c-d)-e ff/g 【解答】 设在表达式计算时各运算符的优先规则如上一题所示。因为直接对中缀算术表达式求值时必须使用两 个栈,分别对运算符和运算对象进行处理,假设命名运算符栈为OPTR( operator的缩写),运算对象栈为
第 4 章 栈与队列 39 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 也叫做栈内(in stack priority)优先数,icp 也叫做栈外(in coming priority)优先数。当刚扫描到的 运算符 ch 的 icp(ch)大于 isp(stack)时,则 ch 进栈;当刚扫描到的运算符 ch 的 icp(ch)小于 isp(stack)时, 则位于栈顶的运算符退栈并输出。从表中可知,icp(“(”)最高,但当“(”进栈后,isp(“(”)变得极低。其它 运算符进入栈中后优先数都升 1,这样可体现在中缀表达式中相同优先级的运算符自左向右计算的要求。 运算符优先数相等的情况只出现在括号配对“)”或栈底的“;”号与输入流最后的“;”号配对时。前者将 连续退出位于栈顶的运算符,直到遇到“(”为止。然后将“(”退栈以对消括号,后者将结束算法。 步序 扫描项 项类型 动 作 栈的变化 输 出 0 ';' 进栈, 读下一符号 ; 1 a 操作数 直接输出, 读下一符号 ; A 2 * 操作符 isp ( ' ; ' ) icp ( ' - ' ), 退栈输出 ; a x * isp ( ' ; ' ) icp ( ' ; ' ), 退栈输出 ; -/ a x * b x 2↑ isp ( ' / ' ) > icp ( ' ; ' ), 退栈输出 ; - a x * b x 2↑/ isp ( ' - ' ) > icp ( ' ; ' ), 退栈输出 ; a x * b x 2↑/ - 结束 4-8 试利用运算符优先数法,画出对如下中缀算术表达式求值时运算符栈和运算对象栈的变化。 a + b * (c - d) - e↑f / g 【解答】 设在表达式计算时各运算符的优先规则如上一题所示。因为直接对中缀算术表达式求值时必须使用两 个栈,分别对运算符和运算对象进行处理,假设命名运算符栈为 OPTR (operator 的缩写),运算对象栈为
第4章栈与队列 OPND(operand的缩写),下面给出对中缀表达式求值的一般规则 (1)建立并初始化OPIR栈和OPND栈,然后在OPTR栈中压入一个“;” (2)从头扫描中缀表达式,取一字符送入ch (3)当ch不等于“;”时,执行以下工作,否则结束算法。此时在OPND栈的栈顶得到运算结果 ①如果ch是运算对象,进OPND栈,从中缀表达式取下一字符送入ch: ②如果ch是运算符,比较ch的优先级icp(ch)和OPTR栈顶运算符isp(OPIR)的优先级 若icp(ch)>ip(OPIR),则ch进OPIR栈,从中缀表达式取下一字符送入ch 若icp(ch)isp(+'),进OPIR栈,取下一符号ab 操作符一 )>isp(“*”),进OPIR栈,取下一符号 进OPND栈,取下一符号 操作符|→ >sp(“(,进OPTR栈,取下一符号 进OPND栈,取下一符 )操作符-ip()”)sp(;”)进OPIR栈,取下一符号 e进OPND栈,取下 操作符一 ↑’)>is ),进OPTR栈,取下一符号 操作数一f进OPND栈,取下一符号 set 操作符|icp(/)<isp(↑”),退OPND栈f,退 OPNDs3s 栈‘e,退OPIR栈‘↑’,计算e↑f→s4,结果 进OPIR栈,取下一符号 OPND栈,取下一符号 20 操作符ip(;")<isp(/),退OPND栈'g,退OPND 栈‘s,退OPTR栈‘/,计算s4/g→s,结果 进OPND栈
第 4 章 栈与队列 40 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) isp (‘ ; ’ ), 进 OPTR 栈, 取下一符号 a ; + 3 b 操作数 b 进 OPND 栈, 取下一符号 a b ; + 4 * 操作符 icp (‘ * ’ ) > isp (‘ + ’ ), 进 OPTR 栈, 取下一符号 a b ; + * 5 ( 操作符 icp (‘ ( ’ ) > isp (‘ * ’ ), 进 OPTR 栈, 取下一符号 a b ; + * ( 6 c 操作数 c 进 OPND 栈, 取下一符号 a b c ; + * ( 7 - 操作符 icp (‘ - ’ ) > isp (‘ ( ’ ), 进 OPTR 栈, 取下一符号 a b ; + * ( - 8 d 操作数 d 进 OPND 栈, 取下一符号 a b c d ; + * ( - 9 ) 操作符 icp (‘ ) ’ ) isp (‘ ; ’ ), 进 OPTR 栈, 取下一符号 s3 ; - 14 e 操作数 e 进 OPND 栈, 取下一符号 s3 e ; - 15 ↑ 操作符 icp (‘↑’ ) > isp (‘ - ’ ), 进 OPTR 栈, 取下一符号 s3 e ; -↑ 16 f 操作数 f 进 OPND 栈, 取下一符号 s3 e f ; -↑ 17 / 操作符 icp (‘ / ’ ) isp (‘ - ’ ), 进 OPTR 栈, 取下一符号 s3 s4 ; - / 19 g 操作数 g 进 OPND 栈, 取下一符号 s3 s4 g ; - / 20 ; 操作符 icp (‘ ; ’ ) < isp (‘ / ’ ), 退 OPND 栈 ‘g’, 退 OPND 栈 ‘s4’, 退 OPTR 栈 ‘/’, 计算 s4 / g → s5, 结果 进 OPND 栈 s3 s5 ; -
第4章栈与队列 上 ip(;") template class Queue ∥循环队列的类定义 Queue( int10 ) Queue()i delete [elements; 3 void EnQueue( Type item ) Type DeQueue (: void Makeempty ((length =0;1 置空队列 int IsEmpty (const i return length =0; 判队列空否 int IsFull const( return ∥判队列满 private int rear, length; /队尾指针和队列长度 ∥放队列元素的数组 nt max size /队列最大可容纳元素个数 构造函数 Queue: Queue( int sz ) rear(maxSize-1), length(O), max Size( z)i ∥建立一个最大具有 maxSize个元素的空队列 elements = new Type]: ∥创建队列空间 assert( elements [=0 断言:动态存储分配成功与否 插入函数 template yoid Queue : EnQueue(Type &item )i ssert(! IsFull () ∥判队列是否不满,满则出错处理 度加1 rear=( rear +1)% maxi /队尾位置进 ∥进队列 删除函数 template (){
第 4 章 栈与队列 41 21 同上 同上 icp (‘ ; ’ ) template class Queue { //循环队列的类定义 public: Queue ( int=10 ); ~Queue ( ) { delete [ ] elements; } void EnQueue ( Type & item ); Type DeQueue ( ); Type GetFront ( ); void MakeEmpty ( ) { length = 0; } //置空队列 int IsEmpty ( ) const { return length == 0; } //判队列空否 int IsFull ( ) const { return length == maxSize; } //判队列满否 private: int rear, length; //队尾指针和队列长度 Type *elements; //存放队列元素的数组 int maxSize; //队列最大可容纳元素个数 } 构造函数 template Queue:: Queue ( int sz ) : rear (maxSize-1), length (0), maxSize (sz) { //建立一个最大具有 maxSize 个元素的空队列。 elements = new Type[maxSize]; //创建队列空间 assert ( elements != 0 ); //断言: 动态存储分配成功与否 } 插入函数 template void Queue :: EnQueue ( Type &item ) { assert ( ! IsFull ( ) ); //判队列是否不满,满则出错处理 length++; //长度加 1 rear = ( rear +1) % maxSize; //队尾位置进 1 elements[rear] = item; //进队列 } 删除函数 template Type Queue :: DeQueue ( ) {
第4章栈与队列 assert(! IsEmpty () 判断队列是否不空,空则出错处理 /队列长度减1 return elements[(rear-length+ maxSize)% maxSize]: 返回原队头元素值 读取队头元素值函数 template Type Queue: GetFront ()i assert(! IsEmpty () return elements[(rear-length+ 1+maxs ize)% maxSize]: /返回队头元素值 4-10假设以数组Q[m]存放循环队列中的元素,同时设置一个标志tag,以tag=0和tag=1来区别在队 头指针( front)和队尾指针(rear)相等时,队列状态为“空”还是“满”。试编写与此结构相应的插入( enqueue)和 删除( dequeue)算法。 【解答】 循环队列类定义 #include template class Queue 环队列的类定义 Queue( int=10); Queue(i delete [Q: 3 void EnQueue( Type item ) Type getFront(片 void MakeEmpty (i front =rear=tag=0; 3 置空队列 int Is Empty) const{ return front=rear&&tag=0;}∥判队列空否 int IsFull (const{ return front=rear&&tag=1;}/判队列满否 int rear, front, tag: 队尾指针、队头指针和队满标志 ∥存放队列元素的数组 int m; 队列最大可容纳元素个数 构造函数 template : Queue( int Sz ) rear(), front(O), tag(O), m(sz)i ∥建立一个最大具有m个元素的空队列 ∥创建队列空间 assert(Q=0; 断言:动态存储分配成功与否 插入函数 assert(!IsFu()片; ∥判队列是否不满,满则出错处理 ear=( rear+1)% /队尾位置进1,队尾指针指示实际队尾位置
第 4 章 栈与队列 42 assert ( ! IsEmpty ( ) ); //判断队列是否不空,空则出错处理 length--; //队列长度减 1 return elements[(rear-length+maxSize) % maxSize]; //返回原队头元素值 } 读取队头元素值函数 template Type Queue :: GetFront ( ) { assert ( ! IsEmpty ( ) ); return elements[(rear-length+1+maxSize) % maxSize]; //返回队头元素值 } 4-10 假设以数组 Q[m]存放循环队列中的元素, 同时设置一个标志 tag,以 tag == 0 和 tag == 1 来区别在队 头指针(front)和队尾指针(rear)相等时,队列状态为“空”还是“满”。试编写与此结构相应的插入(enqueue)和 删除(dlqueue)算法。 【解答】 循环队列类定义 #include template class Queue { //循环队列的类定义 public: Queue ( int=10 ); ~Queue ( ) { delete [ ] Q; } void EnQueue ( Type & item ); Type DeQueue ( ); Type GetFront ( ); void MakeEmpty ( ) { front = rear = tag = 0; } //置空队列 int IsEmpty ( ) const { return front == rear && tag == 0; } //判队列空否 int IsFull ( ) const { return front == rear && tag == 1; } //判队列满否 private: int rear, front, tag; //队尾指针、队头指针和队满标志 Type *Q; //存放队列元素的数组 int m; //队列最大可容纳元素个数 } 构造函数 template Queue:: Queue ( int sz ) : rear (0), front (0), tag(0), m (sz) { //建立一个最大具有 m 个元素的空队列。 Q = new Type[m]; //创建队列空间 assert ( Q != 0 ); //断言: 动态存储分配成功与否 } 插入函数 template void Queue :: EnQueue ( Type &item ) { assert ( ! IsFull ( ) ); //判队列是否不满,满则出错处理 rear = ( rear + 1 ) % m; //队尾位置进 1, 队尾指针指示实际队尾位置
第4章栈与队列 ∥进队列 ∥标志改1,表示队列不空 删除函数 template Type Queue: DeQueue (t assert(! IsEmpty () ∥判断队列是否不空,空则出错处理 front=( front+1)% m; /队头位置进1,队头指针指示实际队头的前一位置 ∥标志改0,表示栈不满 return Front]: ∥返回原队头元素的值 读取队头元素函数 template Type Queue: GetFront (i assert(! IsEmpty () ∥判断队列是否不空,空则出错处理 return Q[(front +1)%m]; 返回队头元素的值 4-11若使用循环链表来表示队列,p是链表中的一个指针。试基于此结构给出队列的插入( enq ueue)和删除 ( dequeue)算法,并给出p为何值时队列空 【解答】 链式队列的类定义 template class Queue ∥链式队列类的前视定义 template class QueueNode i ∥链式队列结点类定义 friend class Queue ∥数据域 QueueNode *link QueueNode( Type d=0, QueueNode=NULL): data(d,link①){}∥构造函数 template class Queue i ∥链式队列类定义 publie Queue(: p(NULL)0 ∥构造函数 Queue ( ∥析构函数 加入到队列中 Type DeQueue ( ∥删除并返回队头元素 Type GetFront ( 查看队头元素的值 ∥置空队列,实现与~ Queue()相同 int IsEmpty const return p= NULL; 1 ∥判队列空否 QueueNode*p; /队尾指针(在循环链表中)
第 4 章 栈与队列 43 Q[rear] = item; //进队列 tag = 1; //标志改 1,表示队列不空 } 删除函数 template Type Queue :: DeQueue ( ) { assert ( ! IsEmpty ( ) ); //判断队列是否不空,空则出错处理 front = ( front + 1 ) % m; //队头位置进 1, 队头指针指示实际队头的前一位置 tag = 0; //标志改 0, 表示栈不满 return Q[front]; //返回原队头元素的值 } 读取队头元素函数 template Type Queue :: GetFront ( ) { assert ( ! IsEmpty ( ) ); //判断队列是否不空,空则出错处理 return Q[(front + 1) % m]; //返回队头元素的值 } 4-11 若使用循环链表来表示队列,p 是链表中的一个指针。试基于此结构给出队列的插入(enqueue)和删除 (dequeue)算法,并给出 p 为何值时队列空。 【解答】 链式队列的类定义 template class Queue; //链式队列类的前视定义 template class QueueNode { //链式队列结点类定义 friend class Queue; private: Type data; //数据域 QueueNode *link; //链域 public: QueueNode ( Type d = 0, QueueNode *l = NULL ) : data (d), link (l) { } //构造函数 }; template class Queue { //链式队列类定义 public: Queue ( ) : p ( NULL ) { } //构造函数 ~Queue ( ); //析构函数 void EnQueue ( const Type & item ); //将 item 加入到队列中 Type DeQueue ( ); //删除并返回队头元素 Type GetFront ( ); //查看队头元素的值 void MakeEmpty ( ); //置空队列,实现与~Queue ( ) 相同 int IsEmpty ( ) const { return p == NULL; } //判队列空否 private: QueueNode *p; //队尾指针(在循环链表中)
第4章栈与队列 }; 队列的析构函数 template Queue: -Queue (i /队列的析构函数 QueueNodelink; delete s;}/逐个删除队列中的结点 队列的插入函数 template void Queue: En Queue( const Type item )i if p= null)i 队列空,新结点成为第一个结点 p=new QueueNode( item, NULL ) p->link p 队列不空,新结点链入p之后 QueueNode 's=new QueueNode( item, NULL s->link=p->link;p=p->link=s;结点p指向新的队尾 队列的删除函数 template Type Queue: DeQueue (i if(p=NUL){coutdata: delete s; ∥保存原队头结点中的值,释放原队头结点 return revalue: ∥返回数据存放地址 队空条件p=NULL 4-12若将一个双端队列顺序表示在一维数组Ⅴ[m]中,两个端点设为endl和end2,并组织成一个循环队 列。试写出双端队列所用指针endl和end2的初始化条件及队空与队满条件,并编写基于此结构的相应的 插入( enqueue)新元素和删除( dEqueue)算 【解答】 初始化条件endl=end2=0; 队空条件endl=end2 队满条件(end1+1)%m=end2;∥设endl端顺时针进栈,end2端逆时针进栈 循环队列类定义 #include template class DoubleQueue i ∥循环队列的类定义 DoubleQueue (i delete [V;) void EnQueue Type item, const int end )i Type DeQueue(const int end ) Type GetFront(const int end ) void Make empty (fendI =end2=0; 1 ∥置空队列 int IsEmpty ( const i return endl == end2; I ∥判两队列空否
第 4 章 栈与队列 44 }; 队列的析构函数 template Queue::~Queue ( ) { //队列的析构函数 QueueNode *s; while ( p != NULL ) { s = p; p = p->link; delete s; } //逐个删除队列中的结点 } 队列的插入函数 template void Queue::EnQueue ( const Type & item ) { if ( p == NULL ) { //队列空, 新结点成为第一个结点 p = new QueueNode ( item, NULL ); p->link = p; } else { //队列不空, 新结点链入 p 之后 QueueNode *s = new QueueNode ( item, NULL ); s->link = p->link; p = p->link = s; //结点 p 指向新的队尾 } } 队列的删除函数 template Type Queue::DeQueue ( ) { if ( p == NULL ) { cout *s = p; //队头结点为 p 后一个结点 p->link = s->link; //重新链接, 将结点 s 从链中摘下 Type retvalue = s->data; delete s; //保存原队头结点中的值, 释放原队头结点 return retvalue; //返回数据存放地址 } 队空条件 p == NULL。 4-12 若将一个双端队列顺序表示在一维数组 V[m]中,两个端点设为 end1 和 end2,并组织成一个循环队 列。试写出双端队列所用指针 end1 和 end2 的初始化条件及队空与队满条件,并编写基于此结构的相应的 插入(enqueue)新元素和删除(dlqueue)算法。 【解答】 初始化条件 end1 = end2 = 0; 队空条件 end1 = end2; 队满条件 ( end1 + 1 ) % m = end2; //设 end1 端顺时针进栈,end2 端逆时针进栈 循环队列类定义 #include template class DoubleQueue { //循环队列的类定义 public: DoubleQueue ( int=10 ); ~DoubleQueue ( ) { delete [ ] V; } void EnQueue ( Type & item, const int end ); Type DeQueue (const int end ); Type GetFront (const int end ); void MakeEmpty ( ) { end1 = end2 = 0; } //置空队列 int IsEmpty ( ) const { return end1 == end2; } //判两队列空否 end1 end2
第4章栈与队列 int IsFull (const{ return(endl+1)%m=end2;}/判两队列满否 int endl, end2 队列两端的指针 ∥放队列元素的数组 /队列最大可容纳元素个 构造函数 DoubleQueue: DoubleQueue( int sz ) endl(O), end2(0), m(sz)i ∥建立一个最大具有m个元素的空队列 ∥)创建队列空间 assert(V=0); 断言:动态存储分配成功与否 插入函数 emplate void Double Queue: EnQueue( Type &item, const int end)( assert(!IsFull () endl = endI +1)% end端指针先进1,再按指针进栈 Lendl=item endl指向实际队头位置 Vlend2]=item ∥end2端先进队列,指针再进1 end2 = end2 -1 +m)%m; end2指向实际队头的下一位置 删除函数 plate assert(!IsEmpty () Type& temp if( end==1)i temp= Lendl] ∥先保存原队头元素的值,endl端指针退1 endl = endI m-1)%m; se i end2 =( end2+1)% temp =vlend2 ∥end2端指针先退1。再保存原队头元素的值 读取队头元素的值 plate Type DoubleQueue GetFront( const int end)(
第 4 章 栈与队列 45 int IsFull ( ) const { return (end1+1) % m == end2; } //判两队列满否 private: int end1, end2; //队列两端的指针 Type *V; //存放队列元素的数组 int m; //队列最大可容纳元素个数 } 构造函数 template DoubleQueue:: DoubleQueue ( int sz ) : end1 (0), end2 (0), m (sz) { //建立一个最大具有 m 个元素的空队列。 V = new Type[m]; //创建队列空间 assert ( V != 0 ); //断言: 动态存储分配成功与否 } 插入函数 template void DoubleQueue :: EnQueue ( Type &item, const int end ) { assert ( !IsFull ( ) ); if ( end == 1 ) { end1 = ( end1 + 1 ) % m; //end1 端指针先进 1, 再按指针进栈 V[end1] = item; //end1 指向实际队头位置 } else { V[end2] = item; //end2 端先进队列, 指针再进 1 end2 = ( end2 - 1 + m ) % m; //end2 指向实际队头的下一位置 } } 删除函数 template Type DoubleQueue :: DeQueue ( const int end ) { assert ( !IsEmpty ( ) ); Type& temp; if ( end == 1 ) { temp = V[end1]; //先保存原队头元素的值, end1 端指针退 1 end1 = ( end1 + m - 1 ) % m; } else { end2 = ( end2 + 1 ) % m; temp = V[end2]; //end2 端指针先退 1。再保存原队头元素的值 } return temp; } 读取队头元素的值 template Type DoubleQueue :: GetFront ( const int end ) {