1、第三章 栈与队列 习题及答案第三章 栈与队列 习题及答案一、基础知识题3.1 设将整数1,2,3,4依次进栈,但只要出栈时栈非空,则可将出栈操作按任何次序夹入其中,请回答下述问题: (1)若入、出栈次序为Push(1), Pop(),Push(2),Push(3), Pop(), Pop( ),Push(4), Pop( ),则出栈的数字序列为何(这里Push(i)表示i进栈,Pop( )表示出栈)? (2) 能否得到出栈序列1423和1432?并说明为什么不能得到或者如何得到。 (3)请分析 1,2 ,3 ,4 的24种排列中,哪些序列是可以通过相应的入出栈操作得到的。 3.2 链栈中为何不
2、设置头结点?答:链栈不需要在头部附加头结点,因为栈都是在头部进行操作的,如果加了头结点,等于要对头结点之后的结点进行操作,反而使算法更复杂,所以只要有链表的头指针就可以了。3.3 循环队列的优点是什么? 如何判别它的空和满? 答:循环队列的优点是:它可以克服顺序队列的假上溢现象,能够使存储队列的向量空间得到充分的利用。判别循环队列的空或满不能以头尾指针是否相等来确定,一般是通过以下几种方法:一是另设一布尔变量来区别队列的空和满。二是少用一个元素的空间。每次入队前测试入队后头尾指针是否会重合,如果会重合就认为队列已满。三是设置一计数器记录队列中元素总数,不仅可判别空或满,还可以得到队列中元素的个
3、数。3.4 设长度为n的链队用单循环链表表示,若设头指针,则入队出队操作的时间为何? 若只设尾指针呢? 答:当只设头指针时,出队的时间为1,而入队的时间需要n,因为每次入队均需从头指针开始查找,找到最后一个元素时方可进行入队操作。若只设尾指针,则出入队时间均为1。因为是循环链表,尾指针所指的下一个元素就是头指针所指元素,所以出队时不需要遍历整个队列。3.5 指出下述程序段的功能是什么? (1) void Demo1(SeqStack *S) int i; arr64 ; n=0 ; while ( StackEmpty(S) arrn+=Pop(S); for (i=0, i n; i+) P
4、ush(S, arri); /Demo1(2) SeqStack S1, S2, tmp; DataType x; ./假设栈tmp和S2已做过初始化 while ( ! StackEmpty (&S1) x=Pop(&S1) ; Push(&tmp,x); while ( ! StackEmpty (&tmp) ) x=Pop( &tmp); Push( &S1,x); Push( &S2, x); (3) void Demo2( SeqStack *S, int m) / 设DataType 为int 型 SeqStack T; int i; InitStack (&T); while (
5、! StackEmpty( S) if( i=Pop(S) !=m) Push( &T,i); while (! StackEmpty( &T) i=Pop(&T); Push(S,i); (4)void Demo3( CirQueue *Q) / 设DataType 为int 型 int x; SeqStack S; InitStack( &S); while (! QueueEmpty( Q ) x=DeQueue( Q); Push( &S,x); while (! StackEmpty( &s) x=Pop(&S); EnQueue( Q,x ); / Demo3(5) CirQueu
6、e Q1, Q2; / 设DataType 为int 型 int x, i , m = 0; . / 设Q1已有内容, Q2已初始化过 while ( ! QueueEmpty( &Q1) ) x=DeQueue( &Q1 ) ; EnQueue(&Q2, x); m+; for (i=0; i n; i+) x=DeQueue(&Q2) ; EnQueue( &Q1, x) ; EnQueue( &Q2, x); 二、算法设计题 3.6 回文是指正读反读均相同的字符序列,如abba和abdba均是回文,但good不是回文。试写一个算法判定给定的字符向量是否为回文。(提示:将一半字符入栈) 3
7、.7 利用栈的基本操作,写一个将栈S中所有结点均删去的算法void ClearStack( SeqStack *S),并说明S为何要作为指针参数? 3.8 利用栈的基本操作, 写一个返回S中结点个数的算法 int StackSize( SeqStack S),并说明S为何不作为指针参数? 3.9 设计算法判断一个算术表达式的圆括号是否正确配对。 (提示: 对表达式进行扫描,凡遇到(就进栈,遇)就退掉栈顶的(,表达式被扫描完毕,栈应为空。 3.10 一个双向栈S是在同一向量空间内实现的两个栈,它们的栈底分别设在向量空间的两端。 试为此双向栈设计初始化InitStack ( S ) 、入栈Push
8、( S , i , x) 和出栈Pop( S , i )等算法, 其中i为0 或1, 用以表示栈号。 3.11 Ackerman 函数定义如下:请写出递归算法。 n+1 当m=0时 AKM ( m , n ) = AKM( m-1 ,1) 当m0 ,n=0时 AKM( m-1, AKM( m,n-1) 当m0, n 0时 3.12 用第二种方法 ,即少用一上元素空间的方法来区别循环队列的队空和队满,试为其设计置空队,判队空,判队满、出队、入队及取队头元素等六个基本操作的算法。 3.13 假设以带头结点的循环链表表示队列,并且只设一个指针指向队尾元素站点(注意不设头指针) ,试编写相应的置空队、
9、判队空 、入队和出队等算法。 3.14 对于循环向量中的循环队列,写出求队列长度的公式。 3.15 假设循环队列中只设rear和quelen 来分别指示队尾元素的位置和队中元素的个数,试给出判别此循环队列的队满条件,并写出相应的入队和出队算法,要求出队时需返回队头元素。 答案:3.2 答:链栈不需要在头部附加头结点,因为栈都是在头部进行操作的,如果加了头结点,等于要对头结点之后的结点进行操作,反而使算法更复杂,所以只要有链表的头指针就可以了。3.3 答:循环队列的优点是:它可以克服顺序队列的假上溢现象,能够使存储队列的向量空间得到充分的利用。判别循环队列的空或满不能以头尾指针是否相等来确定,一
10、般是通过以下几种方法:一是另设一布尔变量来区别队列的空和满。二是少用一个元素的空间。每次入队前测试入队后头尾指针是否会重合,如果会重合就认为队列已满。三是设置一计数器记录队列中元素总数,不仅可判别空或满,还可以得到队列中元素的个数。3.4答:当只设头指针时,出队的时间为1,而入队的时间需要n,因为每次入队均需从头指针开始查找,找到最后一个元素时方可进行入队操作。若只设尾指针,则出入队时间均为1。因为是循环链表,尾指针所指的下一个元素就是头指针所指元素,所以出队时不需要遍历整个队列。3.5 答: (1)程序段的功能是将一栈中的元素按反序重新排列,也就是原来在栈顶的元素放到栈底,栈底的元素放到栈顶
11、。此栈中元素个数限制在64个以内。(2)程序段的功能是利用tmp栈将一个非空栈的所有元素按原样复制到一个空栈当中去。(3)程序段的功能是将一个非空栈中值等于m的元素全部删去。(4)程序段的功能是将一个循环队列反向排列,原来的队头变成队尾,原来的队尾变成队头。(5)首先指出程序可能有印刷错误,for语句中的n应为m才对。这段程序的功能是将队列1的所有元素复制到队列2中去,但其执行过程是先把队列1的元素全部出队,进入队列2,然后再把队列2的元素复制到队列1中。3.6 解:根据提示,算法可设计为:/ishuiwen.h 存为头文件int IsHuiwen( char *S) SeqStack T;
12、int i , l; char t; InitStack( &T); l=strlen(S); /求向量长度 for ( i=0; il/2; i+) /将一半字符入栈 Push( &T, Si); while( !EmptyStack( &T) / 每弹出一个字符与相应字符比较 t=Pop (&T); if( t!=Sl-i) return 0 ;/ 不等则返回0 i-; return -1 ; / 比较完毕均相等则返回 -1/ 以下程序用于验证上面的算法/以下是栈定义( 存为stack.h)/出错控制函数#include #include void Error(char * message
13、) fprintf(stderr, Error: %sn,message); exit(1);/ 定义栈类型#define StackSize 100typedef char Datatype;typedef struct Datatype dataStackSize; int Top; SeqStack;void InitStack( SeqStack *S) /初始化(置空栈) S-Top=-1;int EmptyStack(SeqStack *S) /判栈空 return S-Top = -1; int FullStack (SeqStack *S) / 判栈满 return S-Top
14、=StackSize-1; void Push (SeqStack *S , Datatype x) /进栈 if(FullStack(S) Error(Stack overflow); S-data+S-Top=x;Datatype Pop(SeqStack *S) / 出栈(退栈) if (EmptyStack( S) ) Error( Stack underflow); return S-dataS-Top-;/取栈顶元素(略)/-/以下是主程序#include #include #include stack.h#include ishuiwen.hvoid main( ) char S
15、tr100=; printf(输入一个字符串:n); scanf(%s,Str); if( IsHuiwen(Str) printf( n这个字符串是回文。); else printf(n这个字符串不是回文。);3.7解: 算法如下void ClearStack (SeqStack *S) / 删除栈中所有结点 S-Top = -1; /其实只是将栈置空因为我们要置空的是栈S,如果不用指针来做参数传递,那么函数进行的操作不能对原来的栈产生影响,系统将会在内存中开辟另外的单元来对形参进行函数操作。结果等于什么也没有做。所以想要把函数操作的结果返回给实参的话,就只能用指针来做参数传递了。3.8 解
16、:算法如下:int StackSize (SeqStack S) /计算栈中结点个数 int n=0; if(!EmptyStack(&S) Pop(&S); n+; return n;类似于上面的原因,我们要计算栈中元素个数就要弹出元素才能数得出来,那如果用指针做参数的话,就会把原来的栈中元素弹光,要恢复还得用别的办法给它装回去,而不用指针做参数,则可以避免对原来的栈中元素进行任何操作,系统会把原来的栈按值传递给形参,函数只对形参进行操作,最后返回元素个数就可以了。3.9 解:根据提示,可以设计算法如下:#include #include stack.hint PairBracket( ch
17、ar *S) /检查表达式中括号是否配对 int i; SeqStack T; /定义一个栈 InitStack (&T); for (i=0; itop0 = -1; S-top1 = StackSize; /这里的top2也指出了向量空间,但由于是作为栈底,因此不会出错 int EmptyStack( DblStack *S, int i ) /判栈空(栈号 i) return (i = 0 & S-top0 = -1| i = 1 & S-top1= StackSize) ;int FullStack( DblStack *S) /判栈满,满时肯定两头相遇 return (S-top0
18、= S-top1-1);void Push(DblStack *S, int i, Datatype x) /进栈(栈号i) if (FullStack( S ) Error(Stack overflow);/上溢、退出运行 if ( i = 0) S-Data + S-top0= x; /栈0入栈 if ( i = 1) S-Data - S-top1= x; / 栈1入栈Datatype Pop(DblStack *S, int i) /出栈(栈号i) if (EmptyStack ( S,i) ) Error(Stack underflow);/下溢退出 if( i=0 ) return
19、 ( S-Data S-top0- );/返回栈顶元素,指针值减1 if( i=1 ) return ( S-Data S-top1+ ); /因为这个栈是以另一端为底的,所以指针值加1。/其余算法略 ,以上算法没有上机调试过,请学友自行验证一下。主要是我们理解了算法也就可以了。3.11 解:算法如下int AKM( int m, int n) if ( m= 0) return n+1; if ( m0 & n=0 ) return AKM( m-1, 1); if ( m0 & n0 ) return AKM( m-1, AKM( m, n-1);3.12 解:算法设计如下:/存为Queu
20、e2.h文件void InitQueue ( CirQueue *Q) / 置空队 Q-front=Q-rear=0;int EmptyQueue( CirQueue *Q) /判队空 return Q-front=Q-rear;int FullQueue( CirQueue *Q) / 判队满/如果尾指针加1后等于头指针,则认为满 return (Q-rear+1)%QueueSize= Q-front;Datatype DeQueue( CirQueue *Q) /出队 if(EmptyQueue(Q) Error(队已空,无元素可以出队); Datatype temp; temp=Q-D
21、ataQ-front ;/保存元素值 Q-front= ( Q-front+1 ) %QueueSize;/循环意义上的加1 return temp; /返回元素值void EnQueue (CirQueue *Q, Datatype x) / 入队 if( FullQueue( Q) Error (队已满,不可以入队); Q-DataQ-rear=x; Q-rear=(Q-rear+1)%QueueSize; /rear 指向下一个空元素位置 Datatype FrontQueue( CirQueue *Q) /取队头元素 if (EmptyQueue( Q) Error( 队空,无元素可取
22、); return Q-DataQ-front; /为了验证上述算法是否正确,晓津设计了以下程序/循环队列的定义 存入StructQ.h文件中#define QueueSize 10 /为便与验证,这里假设为10个元素的空间typedef char Datatype ; /设元素的类型为char型typedef struct int front; int rear; Datatype DataQueueSize;CirQueue;/以下是主程序,用来验证算法#include #include #include StrctQ.h / 包含队列结构#include Queue2.h/包含算法头文件
23、/-出错控制程序#include void Error(char * message) fprintf(stderr, Error: %sn,message); exit(1);/-主函数-void main( ) int i; CirQueue Q;/ 定义一个循环队列 InitQueue( &Q );/初始化队列 printf(please enter characters:n); for (i=0; i QueueSize-1 ; i+) EnQueue(&Q, getchar(); printf(n); if(!EmptyQueue(&Q) /先输出一个头元素 printf(front
24、Data is %c, FrontQueue(&Q); printf(n); while(!EmptyQueue(&Q) /如果非空就把队列中的元素输出 printf(%c,DeQueue(&Q); printf(nPlease enter characters again:n); for(i=0; irear = Q-rear-next;/头结点成为尾结点 Q-rear-next = Q-rear;/形成循环链表int EmptyQueue( LinkQueue *Q) /判队空 /当头结点的next指针指向自己时为空队 return Q-rear-next-next=Q-rear-next
25、;void EnQueue( LinkQueue *Q, Datatype x) /入队 /也就是在尾结点处插入元素 QueueNode *p=(QueueNode *) malloc (sizeof(QueueNode);/申请新结点 p-data=x; p-next=NULL;/初始化结点 Q-rear-next-next=p; / 将新结点链入 p-next=Q-rear; /形成循环链表 Q-rear=p;/将尾指针移至新结点Datatype DeQueue( LinkQueue *Q) /出队 /把头结点之后的元素摘下 Datatype t; QueueNode *p; if(EmptyQueue( Q ) Error(Queue underflow); p=Q-rear-next-next; /将要摘下的结点 x=p-data; /保存结点中数据 Q-rear-next-next=p-next;/摘下结点p free(p);/释放被删结点 return x;3.14解:公式如下: 0 EmptyQueue Queuelen= rear - front rearfront rear+QueueSize-front rearfront QueueSize FullQueue3.15 解:知道了尾指针和元素个数,当然就能知道
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1