c#栈的习题2
—、单项选择题1.栈和队列具有相同的( )。 A.抽象数据类型 B.逻辑结构 C.存储结构 D.运算
2.栈是()。 A.顺序存储的线性结构 B.链式存储的非线性结构 C.限制存取点的线性结构 D.限制存储点的非线性结构
3.()不是栈的基本操作。 A.删除栈顶元素 B.删除栈底元素 C.判断栈是否为空 D.将栈置为空栈
4.假定利用数组 a[n] 顺序存储一个栈,用top表示栈顶指针,top==-1表示桟空,并已知栈未满,当元素x进栈时所执行的操作为( )。 A. a[--top]=x B. a[top--]=x C. a[++top]=x D. a[top++]=x
5.设有一个空栈,栈顶指针为1000H,每个元素需要1个存储单元,在执行Push、Push、 Pop、Push、Pop、Push、Pop、Push操作后,桟顶指针的值为( )。 A. 1002H B. 1003H C. 1004H D. 1005H
6.和顺序栈相比,链栈有一个比较明显的优势是( )。 A.通常不会出现栈满的情况 B.通常不会出现栈空的情况 C.插入操作更容易实现 D.删除操作更容易实现
7.设链表不带头结点且所有操作均在表头进行,则下列最不适合作为链栈的是( )。 A.只有表头结点指针,没有表尾指针的双向循环链表 B.只有表尾结点指针,没有表头指针的双向循环链表 C.只有表头结点指针,没有表尾指针的单向循环链表 D.只有表尾结点指针,没有表头指针的单向循环链表
8.向一个栈顶指针为top的链栈中插入一个x结点,则执行( )。 A. top->next=x B. x->next=top->next; top->next=x C. x->next=top; top=x D. x->next=top, top=top->next
9.链栈执行Pop操作,并将出栈的元素存在x中应该执行( )。 A. x=top; top=top->next B.x=top->data C. top=top->next; x=top->data D. x=top->data; top=top->next
10.经过以下栈的操作后,变量x的值为( )。 InitStack(st); Push(st, a); Push(st, b); Pop(st, x); Top(st,x);
A. a B. b C. NULL D. FALSE
11. 3个不同元素依次进栈,能得到( )种不同的出栈序列。 A. 4 B. 5 C. 6 D. 7
12.设a、b、c、d、e、f以所给的次序迸栈,若在进栈操作时,允许出栈操作,则下面得不到的序列为( )。 A. fedcba B. bcafed C. dcefba D. cabdef
13.用S表示进栈操作,用X表示出栈操作,若元素的进栈顺序是1234,为了得到1342的出栈顺序,相应的S和X的操作序列为( )。
A. SXSXSSXX B. SSSXXSXX C. SXSSXXSX D. SXSSXSXX
14.【2010年计算机联考真题】 若元素a、b、c、d、e、f依次进栈,允许进栈、退栈操作交替进行,但不允许连续3次进行退栈工作,则不可能得到的出栈序列是( )。 A. dcebfa B. cbdaef C. bcaefd D. afedcb
15.设找S和队列Q的初始状态为空,元素e1、e2、e3、e4、e5和e6依次通过栈S,一个元素出栈后即进队列Q,若6个元素出栈的序列是e2、e4、e3、e6、e5、e1,则栈S的容量至少应该是( )。 A. 6 B. 4 C. 3 D. 2
16.【2009年计算机联考真题】 设栈S和队列Q的初始状态均为空,元素abcdefg依次进入栈S。若每个元素出栈后立即进入队列Q,且7个元素出队的顺序是bdefeag,则栈S的容量至少是( )。 A. 1 B. 2 C. 3 D. 4
17.若一个栈的输入序列是1, 2,3, …, n,输出序列的第一个元素是n,则第i个输出元素是( )。 A.不确定 B. n-i C. n-i-1 D. n-i+1
18.一个栈的输入序列为1, 2, 3, …, n,输出序列的第一个元素是i,则第j个输出元素是( )。 A, i-j-1 B. i-j C. j-i+1 D.不确定
19.某栈的输入序列为a、b、c、d,下面的4个序列中,不可能是它的输出序列的是()。 A. a,b,c,d B. c,b,d,a C. d,c,a,b D. a,c,b,d
20.若一个栈的输入序列是P1, P2, …, Pn,其输出序列是1, 2, 3, …, n,若P3=1,则p1的值( )。 A.可能是2 B. 一定是2 C.不可能是2 D.不可能是3
21.若已知一个栈的入栈序列是1、2、3、4。其出栈序列为P1、P2、P3、P4,则P2、P4不可能是( )。 A. 2、4 B. 2、1 C. 4、3 D. 3、4
22.设桟的初始状态为空,当字符序列"n 1 _"作为栈的输入时,输出长度为3,且可用做C语言标识符的序列有( )个。 A. 4 B. 5 C. 3 D. 6
23.【2011年计算机联考真题】 元素a、b、c、d、e依次进入初始为空的栈中,若元素进栈后可停留、可出栈,直到所有元素都出栈,则在所有可能的出栈序列中,以元素d开头的序列个数是()。 A. 3 B. 4 C. 5 D. 6
24.釆用共享栈的好处是( )。 A.减少存取时间,降低发生上溢的可能 B.节省存储空间,降低发生上溢的可能 C.减少存取时间,降低发生下溢的可能 D.节省存储空间,降低发生下溢的可能
25.设有一个顺序共享栈Share[0: n-1],其中第一个栈顶指针top1的初值为-1,第二个栈顶指针top2的初值为n,则判断共享栈满的条件是( )。 A. top2-top1==1 B. top1-top2==1 C. top1==top2 D.以上都不对
二、综合应用题
1.有5个元素,其入栈次序为A、B、C、D、E,在各种可能的出栈次序中,第一个出栈元素为C且第二个出栈元素为D的出栈序列有哪几个?
2.若元素的进栈序列为A、B、C、D、E,运用栈操作,能否得到出栈序列B、C、A、 E、D 和 D、B、A、C、E?为什么?
3.假设以I和O分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,可以操作的序列称为合法序列,否则称为非法序列。
1) 下面所示的序列中哪些是合法的? a. IOIIOIOO b. IOOIOIIO c. IIIOIOIO d. IIIOOIOO
2) 通过对1)的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回 true,否则返回false(假定被判定的操作序列已存入一维数组中)。
4.设单链表的表头指针为h,结点结构由data和next两个域构成,其中data域为字符型。试设计算法判断该链表的前n个字符是否中心对称。例如xyx、xyyx都是中心对称。
5.设有两个栈s1、s2都釆用顺序栈方式,并且共享一个存储区[0, …, maxsize-1],为了尽量利用空间,减少溢出的可能,可釆用栈顶相向、迎面增长的存储方式。试设计s1、s2 有关入栈和出栈的操作算法。
答案与解析
一、单项选择题
1. B 线性表、栈和队列的逻辑结构都是相同的,都属于线性结构,只是它们对数据的运算不同,从而表现出不同的特点。
2. C 首先栈是一种线性表,所以B、D错。按存储结构的不同可分为顺序栈和链栈,但不可以把栈局限在某种存储结构上,所以A错。栈和队列都是限制存取点的线性结构。
3. B 基本操作是指该结构最核心、最基本的运算,其他较复杂的操作可以通过基本操作实现。删除栈底元素不属于栈的基本运算,但它可以通过调用栈的基本运算求得。
4. C 入栈指针top加1,初始时top为-1,则第1个元素入找后,top为0,即指向栈顶元素,故入栈时应先将指针top加1,再将元素入栈,只有C符合题意。
5. A 每个元素需要1个存储单元,所以每入栈一次top加1,出栈一次top减1。指针top的值依次为 1001H,1002H, 1001H, 1002H,1001H,1002H,1001H,1002H。
6. A 顺序栈釆用数组存储,数组的大小是固定的,不能动态地分配大小。和顺序栈相比,链栈的最大优势在于它可以动态地分配存储空间,所以答案为A。
7. C 通常栈的插入和删除在表头进行。对于选项C,插入和删除一个结点后,仍需将其变为循环单链表,因此需要找到其尾结点,时间复杂度为o(n)。
若不做题干中的限制,则栈顶可取表头(带头结点的链表)或第二个结点(不带头结点的链表),找指针的位置取头结点(带头结点的链表)或表头(不带头结点的链表)。
8. C 链栈釆用不带头结点的单链表表示时,进栈橾作在首部插入一个结点x(即x->next=top),插入完后需将top指向该插入的结点X。请思考当链栈存在头结点时的情况。
9. D 这里假设栈顶指针指向的是栈顶元素,所以选D;而A中首先将top指针赋给了 x,错误;B中没有修改top指针的值;C为top指针指向栈顶元素的上一个元素时的答案。
10. A 执行前3句后,栈st内的值为a,b,其中b为栈顶元素,执行第4句后,x的值为b,执行最后一句后x的值为a。
11. B 对于n个不同元素进栈,出栈序列的个数为
![](http://c.biancheng.net/cpp/uploads/allimg/140713/1-140G31FP9127.gif)
上述的公式叫做卡特兰(Catalan)数,可釆用数学归纳法证明,有兴趣的读者可以参考组合数学的教材(知道该公式即可)。考题中给出的n值不会很大,可以根据栈的特点,若Xi已经出栈,则Xi前面的尚未出栈的元素一定逆置有序地出栈,因此可以釆取例举的方法。如a、b、c依次进栈的出栈序列有abc,acb, bac, bca, cba。
12. D 根据栈“先进后出”的特点,并且在进栈操作的同时允许出栈操作,显然,答案D中c 最先出栈,则此时栈内必定为a和b,但由于a先于b进栈,故要晚出栈。对于某个出栈的元素,在它之前进栈却晚出栈的元素必定是按逆序出栈的,其余答案均是可能出现的情况。
此题也可以釆用将各序列逐个代入,以确定是否有对应的进出栈序列(类似下题)。
13. D 釆用排除法,选项A、B、C得到的出栈序列分别为1243、3241、1324。由1234得到 1342的进出栈序列为:1进,1出,2进,3进,3出,4进,4出,2出,故选D。
14. D 选项A可由a进,b进,c进,d进,d出,c出,e进,e出,b出,f进,f出,a出得到; 选项B可由a进,b进,c进,c出,b出,d进,d出,a出,e进,e出,f进,f出得到; 选项C可由a进,b进,b出,c进,c出,a出,d进,e进,e出,f进,f出,d出得到; 选项D可由a进,a出,b进,c进,d进,e进,f进,f出,e出,d出,c出,b出得到,但要求不允许连续3次退栈操作,故选D。
15. C 本题将队列和栈结合起来,由于队列“先进先出”的特性,所以出队的序列与进队的序列是相同的,从而可以得到出栈的次序为e2、e4、e3、e6、e5、e1;再由栈“先进后出”的特性,进栈的次序为e1、e2、e3、e4、e5、e6,可见,排在某个元素之后出栈却排在它之前进栈的元素个数,表示之前栈内的元素个数。因此,e4进栈后,e1和e3在栈中;而后e4 和e3出找;e6进找后,e5和e1也在找中。因此,找的最小容量为3。
16. C 时刻注意栈的特点是先进后出,下表是出入栈的详细过程。
序号 | 说明 | 栈内 | 栈外 | 序号 | 说明 | 栈内 | 栈外 |
---|---|---|---|---|---|---|---|
1 | a入栈 | a | 8 | e入栈 | ae | bdc | |
2 | b入栈 | ab | 9 | f入栈 | aef | bdc | |
3 | b出栈 | a | b | 10 | f出栈 | ae | bdcf |
4 | c入钱 | ac | b | 11 | e出栈 | a | bdcfe |
5 | d入栈 | acd | b | 12 | a出枝 | bdcfea | |
6 | d出栈 | ac | bd | 13 | g入栈 | g | bdcfea |
7 | c出栈 | a | bdc | 14 | g出栈 | bdcfeag |
栈内的最大深度为3,故栈S的容量至少是3。
另解:由于元素的出队顺序和入队顺序是一样的,所以,元素的出栈顺序也就是b、d、 c、f、e、a、g,所以,元素的入栈出栈顺序为 Push(S, a),Push(S, b),Pop(S, b),Push(S, c),Push(S, d),Pop(S, d),Pop(S, c),Push(S, e)。Push(S, f),Pop(S, f),Pop(S,e), Pop(S,a),Push(S, g),Pop(S, g)。假设初始所需容量为0,每做一次Push操作进行一次加1操作,每做一次Pop进行一次减1操作,记录容量的最大值为3,所以答案选C。
17. D 第n个元素先出栈,说明前n-1个元素都已经按顺序入栈,由“先进后出”的特点可知,此时的输出序列一定是输入序列的逆序,故答案选D。
18. D 当第i个元素第一个出栈时,则i之前的元素可以依次排在i之后出栈,但剩余的元素可以在此时进栈并且也会排在i之前的元素出栈,所以,第j个出栈的元素是不确定的。
19. C 对于A,可能的顺序是a入,a出,b入,b出,c入,c出,d入,d出。 对于B,可能的顺序是a入,b入,c入,c出,b出,d入,d出,a出。 对于D,可能的顺序是a入,a 出,b入,c入,c出,b出,d入,d出。 C没有对应的序列。
另解:若出栈序列的第一个元素为d,则出栈序列只能是dcba。该思想通常也适用了出栈序列的局部分析:如12345入栈,问出栈序列34152是否正确?如何分析?若第一个出栈元素是3,则此时12必停留在栈中,它们出栈的相对顺序只能是21,故34152错误。
20. C 入栈序列是P1,P2,…,Pn。由于P3=1,即P1、P2、P3连续入栈后,第一个出栈元素是 P3,说明P1、P2已经按序进栈,根据先进后出的特点可知,P2必定在P1之前出栈,而第二个出栈元素是2,而此时P1必定不是栈顶元素,因为P2尚未出栈。因此P1的值不可能是2。
21. C 对于A,可能的顺序是1入栈,1出栈,2入栈,2出栈,3入栈,3出栈,4入栈,4 出栈。 对于B,可能的顺序是1入栈,2入栈,3入栈,3出找,2出找,4入栈,4出桡,1 出栈。 对于D,可能的顺序是1入栈,1出栈,2入栈,3入栈,3出栈,2出桟,4入栈,4 出栈。 C则没有对应的序列。
22. C 标识符的第一个字符必须是大小写英文字母或者下画线,而不能是数字。按照上述规定"n 1 _"三个字符符合规定的标识符有n1_、n_1、_1n, _n1四种形式。第一种:n进栈再出栈, 1进栈再出栈,_进栈再出栈。第二种:n进栈再出栈,1进栈,_进栈,_出栈,1出栈。第三种:n进栈,1进栈_进栈,_出栈,1出栈,n出栈。而根据栈的操作特性,_n1这种情况不可能出现,故选C。
23. B 出栈顺序必为d_c_b_a,e的顺序不定,在任意一个"_"上都有可能。
另解:d首先出栈,则abc停留在栈中,此时钱的状态如下图所示。此时可以有如下4种操作: ①e进栈后出栈,则出栈序列为decba; ②c出栈,e进桟后出栈,出桟序列为dceba; ③cb出桟,e进栈后出栈,出栈序列为dcbea; ④cba出找,e进栈后出栈,出栈序列为dcbae。
![](http://c.biancheng.net/cpp/uploads/allimg/140713/1-140G31G01H22.jpg)
24. B 存取栈中的元素都只需要0(1)的时间,所以,减少存取时间无从谈起。另外,栈的插入和删除操作都是在栈顶进行的,只可能发生上溢(栈顶指针超出了最大范围),不可能发生下溢(对于上溢、下溢,只需大概了解即可,不必深究),因此本题答案为B。
25. A 这种情况就是前面我们所描述的,详细内容请参见本节考点精析部分对共享栈的讲解。另外,读者可以思考若top1的初值为0,top2的初值为n-1时栈满的条件。
注意:栈顶、队头与队尾的指针的定义是不唯一的,读者务必要仔细审题。
二、综合应用题
1.解答: CD出栈后的状态如下图所示。
此时有如下3种操作: ①E进栈后出栈,则出栈序列为CDEBA; ②B 出栈,E进栈后出栈,出栈序列为CDBEA; ③B出栈,A出栈,E进栈后出栈,出栈序列为CDBAE。
所以,以CD开头的出栈序列有:CDEBA、CDBEA、CDBAE三种。
![](http://c.biancheng.net/cpp/uploads/allimg/140713/1-140G31G2352N.jpg)
2.解答: 能得到出栈序列BCAED。可由A进,B进,B出,C进,C出,A出,D进,E进,E出,D出得到。不能得到出栈序列DBACE。若出栈序列以D开头,说明在D之前的入栈元素是A、B和C,三个元素中C是栈顶元素,B和A不可能早于C出栈,故不可能得到出栈序列DBACE。
3.解答: 1) A、D合法,而B、C不合法。在B中,先入栈1次,再连续出栈2次,错误。在 C中,入栈和出栈次数不一致,会导致最终的栈不空。A、D均为合法序列,请自行模拟。注意:在操作过程中,入栈次数一定大于或等于出栈次数;结束时,栈一定为空。
2) 设被判定的操作序列已存入一维数组A中。算法的基本设计思想:依次逐一扫描入栈出栈序列(即由"I"和"O"组成的字符串),每扫描至任一位置均需检查出栈次数(即 “O”的个数)是否小于入栈次数("I"的个数),若大于则为非法序列。扫描结束后,再判断入栈和出栈次数是否相等,若不相等则不合题意,为非法序列。
- int Judge(char A[]) {
- //判断字符数组A中的输入输出序列是否是合法序列。如是,返回true,否则返回false
- i=0;
- j=k=0; //i为下标,j和k分别为字母I和O的个数
- while (A[i]!='\0') { //未到字符数组尾
- switch(A[i]){
- case 'I' ; j++; break; //入栈次数增 1
- case 'O' : k++;
- if (k>j ) { printf ("序列非法\n") ; exit (0) ; }
- }
- i++; //不论A[i]是“I”或“0”,指针i均后移
- } //while
- if (j!=k) {
- printf ("序列非法\n" );
- return false;
- }else{
- printf ("序列合法\n");
- return true;
- }
- }
另解:入栈后,栈内元素个数加1;出栈后,栈内元素个数减1,因此,可以将判定一组出入栈序列是否合法,转化为一组+1、-1组成的序列,它的任意前缀子序列的累加和不小于0 (每次出栈或入栈操作后判断),则合法;否则,非法。
4.解答: 算法思想:使用栈来判断链表中的数据是否中心对称。将链表的前一半元素依次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一个元素,两个元素比较,若相等,则将链表中下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行。
int dc(LinkList L, int n){ //h是带头结点的n个元素单链表,本算法判断链表是否是中心对称 char s[n/2];int i=1; //i记结点个数,s字符栈 P=L->next; //p是链表的工作指针,指向待处理的当前元素 for{i=0;i<n/2;i++) { //链表前一半元素进栈 s[i]=p->data; p=p->next; } i--; //恢复最后的i值 if(n%2==1) //若n是奇数,后移过中心结点 p=p->next; while(p!=NULL && s[i] ==p->data) { //检测是否中心对称 i--; //i充当找顶指针 p=p->next; } if (i==-1) //桟为空找 return 1; //链表中心对称 else return 0; //链表不中心对称 }
算法先将“链表的前一半”元素(字符)进栈。当n为偶数时,前一半和后一半的个数相同;当n为奇数时,链表中心结点字符不必比较,移动链表指针到下一字符开始比较。比较过程中遇到不相等时,立即退出while循环,不再进行比较。
本题也可以先将单链表中的元素全部入栈,然后再次扫描单链表L并比较,直到比较到单链表L尾为止,但是算法需要两次扫描单链表L,效率不及上述算法高。
5.解答:
两个栈共享向量空间,将两个栈的栈底设在向量两端,初始时,s1栈顶指针为-1,s2 栈顶指针为maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。
#define maxsize // 两个栈共享顺序存储空间所能达到的最多元素数 #define elemtp int //假设元素类型为整型 typedef struct{ elemtp stack[maxsize]; //栈空间 int top [2]; //top为两个栈顶指针 }stk; stk s; //s是如上定义的结构类型变量,为全局变量
本题的关键在于,两个桟入栈和退栈时的栈顶指针的计算。s1栈是通常意义下的栈;而 s2栈入栈操作时,其栈顶指针左移(减1),退栈时,栈顶指针右移(加1)。
此外,对于所有栈的操作,都要注意“入栈判满、出栈判空”的检查。
1) 入栈操作
int push(int i, int x){ //入栈操作。i为栈号,i=0表示左边的s1栈,i=1表示右边的s2栈,x是入栈元素 //入栈成功返回1,否则返回0 if(i<0 || i>1){ printf ("栈号输入不对"); exit(0); } if(s.top[1]-s.top[0]==1){ printf ("栈已满\n"); return 0; } switch(i){ case 0: s.stack[++s.top[0]] = x; return 1; break; case 1: s.stack[--s.top[1]] = x; return 1; } }
2) 退栈操作
elemtp pop(int i) { //退栈算法。i代表栈号,i=0时为s1栈,i=l时为s2栈 //退栈成功返回退栈元素,否则返回-1 if(i<0||i>1){ printf ("栈号输入错误\1n"); exit(0); } switch(i) { case 0: if (s.top[0]==-1) { printf ("栈空\n"); return -1; }else return s.stack[s.top[0]--]; case 1: if(s.top[1]==maxsize){ printf ("栈空\n"); return -1; }else return s.stack[s.top[1]++]; }//switch }
2.1. 对于栈操作数据的原则是(B )。
A. 先进先出 B.后进先出 C.后进后出 D. 不分顺序
1.2.一个栈的输入序列为123…n,若输出序列的第一个元素是n,输出第i(1<=i<=n)个元素是( B )。
A.不确定 B.n-i+1 C. i D. n-i 1.3. 若一个栈的输入序列为1,2,3,…,n,输出序列的第一个元素是i,则第j个输出元素是(D )。
A.i-j-1 B.i-j C.j-i+1 D. 不确定的
1.4. 若已知一个栈的入栈序列是1,2,3,…,n,其输出序列为p1,p2,p3,…,pN,若pN是n,则pi是( D )。
A. i B.n-i C.n-i+1 D. 不确定
1.5. 有六个元素6,5,4,3,2,1 的顺序进栈,问下列哪一个不是合法的出栈序列?( C )。
A. 5 4 3 6 1 2 B. 4 5 3 1 2 6 C. 3 4 6 5 2 1 D. 2 3 4 1 5 6
1.6. 设栈的输入序列是1,2,3,4,则(D )不可能是其出栈序列。
A. 1,2,4,3, B. 2,1,3,4, C. 1,4,3,2, D. 4,3,1,2, E. 3,2,1,4,
1.7. 一个栈的输入序列为1 2 3 4 5,则下列序列中不可能是栈的输出序列的是( B )。
A. 2 3 4 1 5 B. 5 4 1 3 2 C. 2 3 1 4 5 D. 1 5 4 3 2
1.8. 设一个栈的输入序列是 1,2,3,4,5,则下列序列中,是栈的合法输出序列的是( D )。
A. 5 1 2 3 4 B. 4 5 1 3 2 C. 4 3 1 2 5 D. 3 2 1 5 4
1.9. 某堆栈的输入序列为a, b,c ,d,下面的四个序列中,不可能是它的输出序列的是( D )。
A. a,c,b,d B. b, c,d,a C. c, d,b, a D. d, c,a,b
1.10. 设abcdef以所给的次序进栈,若在进栈操作时,允许退栈操作,则下面得不到的序列为( D )。
A.fedcba B. bcafed C. dcefba D. cabdef
1.11. 设有三个元素X,Y,Z顺序进栈(进的过程中允许出栈),下列得不到的出栈排列是( C )。
A.XYZ B. YZX C. ZXY D. ZYX
1.12. 用链接方式存储的队列,在进行删除运算时(D )。
A. 仅修改头指针 B. 仅修改尾指针 C. 头、尾指针都要修改 D. 头、尾指针可能都要修改
1.13. 用不带头结点的单链表存储队列时,其队头指针指向队头结点,其队尾指针指向队尾结点,则在进行删除操作时( D )。
A.仅修改队头指针 B. 仅修改队尾指针 C. 队头、队尾指针都要修改 D. 队头,队尾指针都可能要修改
1.14. 递归过程或函数调用时,处理参数及返回地址,要用一种称为(C )的数据结构。
A.队列 B.多维数组 C.栈 D. 线性表
二、判断题
1. 消除递归不一定需要使用栈,此说法(√ )
2. 栈是实现过程和函数等子程序所必需的结构。(√ )
3. 两个栈共用静态存储空间,对头使用也存在空间溢出问题。(√ )
4.两个栈共享一片连续内存空间时,为提高内存利用率,减少溢出机会,应把两个栈的栈底分别设在这片内存空间的两端。(√ )
5. 即使对不含相同元素的同一输入序列进行两组不同的合法的入栈和出栈组合操作,所得的输出序列也一定相同。(× )
6. 栈与队列是一种特殊操作的线性表。(√ )
7. 若输入序列为1,2,3,4,5,6,则通过一个栈可以输出序列3,2,5,6,4,1. (√ )
8. 栈和队列都是限制存取点的线性结构。( √ )
9.若输入序列为1,2,3,4,5,6,则通过一个栈可以输出序列1,5,4,6,2,3。(× )
10. 任何一个递归过程都可以转换成非递归过程。(√ )
11. 只有那种使用了局部变量的递归过程在转换成非递归过程时才必须使用栈。(× )
12. 队列是一种插入与删除操作分别在表的两端进行的线性表,是一种先进后出型结构。(× )
13. 通常使用队列来处理函数或过程的调用。(× )
14. 队列逻辑上是一个下端和上端既能增加又能减少的线性表。(√ )
15. 循环队列通常用指针来实现队列的头尾相接。(× )
16. 循环队列也存在空间溢出问题。(√ )
17. 队列和栈都是运算受限的线性表,只允许在表的两端进行运算。(× )
18. 栈和队列都是线性表,只是在插入和删除时受到了一些限制。(√ )
19. 栈和队列的存储方式,既可以是顺序方式,又可以是链式方式。(√ )
三、填空 1.栈是操作受限(或限定仅在表尾进行插入和删除操作)的线性表,其运算遵循后进先出的原则。
2.栈 是限定仅在表尾进行插入或删除操作的线性表。
3. 一个栈的输入序列是:1,2,3则不可能的栈输出序列是3 1 2。
4.两个栈共享空间时栈满的条件两栈顶指针值相减的绝对值为1(或两栈顶指针相邻)。。
5.在作进栈运算时应先判别栈是否满;在作退栈运算时应先判别栈是否空;当栈中元素为n个,作进栈运算时发生上溢,则说明该栈的最大容量为n。为了增加内存空间的利用率和减少溢出的可能性,由两个栈共享一片连续的空间时,应将两栈的栈底 分别设在内存空间的两端,这样只有当两栈顶指针相邻(即值之差的绝对值为1)时才产生溢出。
6. 多个栈共存时,最好用链式存储结构作为存储结构。
7.用S表示入栈操作,X表示出栈操作,若元素入栈的顺序为1234,为了得到1342出栈顺序,相应的S和X的操作串为S×SS×S×× 。
8. 循环队列的引入,目的是为了克服假溢出时大量移动数据元素。
9.队列又称作先进先出表。
10. 已知链队列的头尾指针分别是f和r,则将值x入队的操作序列是s=(LinkedList)malloc(sizeof(LNode)); s->data=x;s->next=r->next;r->next=s;r=s;。
11.区分循环队列的满与空,只有两种方法,它们是牺牲一个存储单元和设标记。
四、应用题
1.1. 名词解释:栈。
答:栈是只准在一端进行插入和删除操作的线性表,允许插入和删除的一端叫栈顶,另一端叫栈底。最后插入的元素最先删除,故栈也称后进先出(LIFO)表。
1.2. 名词解释:队列
答:队列是允许在一端插入而在另一端删除的线性表,允许插入的一端叫队尾,允许删除的一端叫队头。最先插入队的元素最先离开(删除),故队列也常称先进先出(FIFO)表。
1.3. 什么是循环队列?
答:用常规意义下顺序存储结构的一维数组表示队列,由于队列的性质(队尾插入和队头删除),容易造成“假溢出”现象,即队尾已到达一维数组的高下标,不能再插入,然而队中元素个数小于队列的长度(容量)。循环队列是解决“假溢出”的一种方法。通常把一维数组看成首尾相接。在循环队列下,通常采用“牺牲一个存储单元”或“作标记”的方法解决“队满”和“队空”的判定问题。
1.4. 假设以S和X分别表示入栈和出栈操作,则对初态和终态均为空的栈操作可由S和X组成的序列表示(如SXSX)。
(1)试指出判别给定序列是否合法的一般规则。
答:通常有两条规则。第一是给定序列中S的个数和X的个数相等;第二是从给定序列的开始,到给定序列中的任一位置,S的个数要大于或等于X的个数。
(2)两个不同合法序列(对同一输入序列)能否得到相同的输出元素序列?如能得到,请举列说明。
答:可以得到相同的输出元素序列。例如,输入元素为A,B,C,则两个输入的合法序列ABC和BAC均可得到输出元素序列ABC。对于合法序列ABC,我们使用本题约定的S×S×S×操作序列;对于合法序列BAC,我们使用SS××S×操作序列。
1.5. 有5 个元素,其入栈次序为:A,B,C,D,E,在各种可能的出栈次序中,以元素C,D最先出栈(即C第一个且D第二个出栈)的次序有哪几个?
答:三个:CDEBA,CDBEA,CDBAE
1.6. 如果输入序列为1 2 3 4 5 6,试问能否通过栈结构得到以下两个序列:4 3 5 6 1 2和1 3 5 4 2 6;请说明为什么不能或如何才能得到。
答:输入序列为123456,不能得出435612,其理由是,输出序列最后两元素是12,前面4个元素(4356)得到后,栈中元素剩12,且2在栈顶,不可能栈底元素1在栈顶元素2之前出栈。 得到135426的过程如下:1入栈并出栈,得到部分输出序列1;然后2和3入栈,3出栈,部分输出序列变为:13;接着4和5入栈,5,4和2依次出栈,部分输出序列变为13542;最后6入栈并退栈,得最终结果135426。
1.7. 若元素的进栈序列为:A、B、C、D、E,运用栈操作,能否得到出栈序列B、C、A、E、D和D、B、A、C、E?为什么?
答: 能得到出栈序列B、C、A、E、D,不能得到出栈序列D、B、A、C、E。其理由为:若出栈序列以D开头,说明在D之前的入栈元素是A、B和C,三个元素中C是栈顶元素,B和A不可能早于C出栈,故不可能得到D、B、A、C、E出栈序列。
1.8. 设输入序列为a,b,c,d,试写出借助一个栈可得到的两个输出序列和两个不能得到的输出序列。
答:借助栈结构,n个入栈元素可得到1/(n+1)((2n)!/(n!*n!))种出栈序列。本题4个元素,可有14种出栈序列,abcd和dcba就是其中两种。但dabc和adbc是不可能得到的两种。
1.9. 设输入序列为2,3,4,5,6,利用一个栈能得到序列2,5,3,4,6吗?栈可以用单链表实现吗?
答:不能得到序列2,5,3,4,6。栈可以用单链表实现,这就是链栈。由于栈只在栈顶操作,所以链栈通常不设头结点。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
五、算法设计题 1.1. 设从键盘输入一整数的序列:a1, a2, a3,…,an,试编写算法实现:用栈结构存储输入的整数,当ai≠-1时,将ai进栈;当ai=-1时,输出栈顶整数并出栈。算法应对异常情况(入栈满等)给出相应的信息。 答:#define maxsize 栈空间容量 void InOutS(int s[maxsize]) //s是元素为整数的栈,本算法进行入栈和退栈操作。 {int top=0; //top为栈顶指针,定义top=0时为栈空。 for(i=1; i<=n; i++) //n个整数序列作处理。 {scanf(“%d”,&x); //从键盘读入整数序列。 if(x!=-1) // 读入的整数不等于-1时入栈。 if(top==maxsize-1){printf(“栈满\n”);exit(0);}else s[++top]=x; //x入栈。 else //读入的整数等于-1时退栈。 {if(top==0){printf(“栈空\n”);exit(0);} else printf(“出栈元素是%d\n”,s[top--]);}} }//算法结束。 1.2. 设表达式以字符形式已存入数组E[n]中,‘#’为表达式的结束符,试写出判断表达式中括号(‘(’和‘)’)是否配对的C语言描述算法:EXYX(E); (注:算法中可调用栈操作的基本算法。) 答:[题目分析]判断表达式中括号是否匹配,可通过栈,简单说是左括号时进栈,右括号时退栈。退栈时,若栈顶元素是左括号,则新读入的右括号与栈顶左括号就可消去。如此下去,输入表达式结束时,栈为空则正确,否则括号不匹配。 int EXYX(char E[],int n) //E[]是有n字符的字符数组,存放字符串表达式,以‘#’结束。本算法判断表达式中圆括号是否匹配。 {char s[30]; //s是一维数组,容量足够大,用作存放括号的栈。 int top=0; //top用作栈顶指针。 s[top]= ‘#’; //‘#’先入栈,用于和表达式结束符号‘#’匹配。 int i=0; //字符数组E的工作指针。 while(E[i]!= ‘#’) //逐字符处理字符表达式的数组。 switch(E[i]) {case‘(’: s[++top]=‘(’; i++ ; break ; case‘)’: if(s[top]==‘(’{top--; i++; break;} else{printf(“括号不配对”);exit(0);} case‘#’: if(s[top]==‘#’){printf(“括号配对\n”);return (1);} else {printf(“ 括号不配对\n”);return (0);} //括号不配对 default : i++; //读入其它字符,不作处理。 } }//算法结束。 [算法讨论]本题是用栈判断括号匹配的特例:只检查圆括号的配对。一般情况是检查花括号(‘{’,‘}’)、方括号(‘[’,‘]’)和圆括号(‘(’,‘)’)的配对问题。编写算法中如遇左括号(‘{’,‘[’,或‘(’)就压入栈中,如遇右括号(‘}’,‘]’,或‘)’),则与栈顶元素比较,如是与其配对的括号(左花括号,左方括号或左圆括号),则弹出栈顶元素;否则,就结论括号不配对。在读入表达式结束符‘#’时,栈中若应只剩‘#’,表示括号全部配对成功;否则表示括号不匹配。 另外,由于本题只是检查括号是否匹配,故对从表达式中读入的不是括号的那些字符,一律未作处理。再有,假设栈容量足够大,因此入栈时未判断溢出。 1.3. 从键盘上输入一个逆波兰表达式,用伪码写出其求值程序。规定:逆波兰表达式的长度不超过一行,以$符作为输入结束,操作数之间用空格分隔,操作符只可能有+、-、*、/四种运算。例如:234 34+2*$ 答:[题目分析]逆波兰表达式(即后缀表达式)求值规则如下:设立运算数栈OPND,对表达式从左到右扫描(读入),当表达式中扫描到数时,压入OPND栈。当扫描到运算符时,从OPND退出两个数,进行相应运算,结果再压入OPND栈。这个过程一直进行到读出表达式结束符$,这时OPND栈中只有一个数,就是结果。 float expr( ) //从键盘输入逆波兰表达式,以‘$’表示输入结束,本算法求逆波兰式表达式的值。 {float OPND[30]; // OPND是操作数栈。 init(OPND); //两栈初始化。 float num=0.0; //数字初始化。 scanf (“%c”,&x);//x是字符型变量。 while(x!=’$’) {switch {case‘0’<=x<=’9’:while((x>=’0’&&x<=’9’)||x==’.’) //拼数 if(x!=’.’) //处理整数 {num=num*10+(ord(x)-ord(‘0’)); scanf(“%c”,&x);} else //处理小数部分。 {scale=10.0; scanf(“%c”,&x); while(x>=’0’&&x<=’9’) {num=num+(ord(x)-ord(‘0’)/scale; scale=scale*10; scanf(“%c”,&x); } }//else push(OPND,num); num=0.0;//数压入栈,下个数初始化 case x=‘ ’:break; //遇空格,继续读下一个字符。 case x=‘+’:push(OPND,pop(OPND)+pop(OPND));break; case x=‘-’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2-x1);break; case x=‘*’:push(OPND,pop(OPND)*pop(OPND));break; case x=‘/’:x1=pop(OPND);x2=pop(OPND);push(OPND,x2/x1);break; default: //其它符号不作处理。 }//结束switch scanf(“%c”,&x);//读入表达式中下一个字符。 }//结束while(x!=‘$’) printf(“后缀表达式的值为%f”,pop(OPND)); }//算法结束。 [算法讨论]假设输入的后缀表达式是正确的,未作错误检查。算法中拼数部分是核心。若遇到大于等于‘0’且小于等于‘9’的字符,认为是数。这种字符的序号减去字符‘0’的序号得出数。对于整数,每读入一个数字字符,前面得到的部分数要乘上10再加新读入的数得到新的部分数。当读到小数点,认为数的整数部分已完,要接着处理小数部分。小数部分的数要除以10(或10的幂数)变成十分位,百分位,千分位数等等,与前面部分数相加。在拼数过程中,若遇非数字字符,表示数已拼完,将数压入栈中,并且将变量num恢复为0,准备下一个数。这时对新读入的字符进入‘+’、‘-’、‘*’、‘/’及空格的判断,因此在结束处理数字字符的case后,不能加入break语句。
以上练习题都是整理自网上的,再次说明,希望大家学习交流,不可用于其他用途