栈相关习题及详解(选择题和综合题) ——数据结构

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_22073849/article/details/78355247

栈的基本概念

栈的定义

:只允许在一端进行插入或删除操作的线性表。首先栈是一种线性表,但是限定这种线性表只能在某一端进行插入和删除操作。

栈顶:线性表允许进行插入和删除的那一端
栈底:固定的,不允许进行插入和删除的另一端
空栈:不含任何元素的空表。

进栈出栈的变化形式

最先进栈的元素,是不是就只能是最后出栈呢

答案是不一定,要看什么情况。栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,也就是说,在不是所有元素都进栈的情况下,进去的元素也可以出栈,只要保证是栈顶元素出栈就可以,那么变化就很多了,举例来说:

如果我们有3个整形数字元素1、2、3依次进栈,会有哪些出栈次序呢?

  • 第一种:1、2、3进,再3、2、1出。这事最简单的最好理解的一种,出栈次序为321
  • 第二种:1进,1出,2进,2出,3进,3出,出栈次序为123
  • 第三种:1进,2进,2出,1出,3进,3出,出栈次序为213
  • 第四种:1进,1出,2进,3进,3出,2出,出栈次序为132
  • 第五种:1进,2进,2出,3进,3出,1出,出栈次序为231

有没有可能是312这样的次序出栈呢,答案是不可能的,因为3先出栈,就意味着3曾经进栈,既然3进栈了,那也意味着1和2已经进栈了,此时,2一定是在1的上面,就是更接近栈顶,那么出栈只可能是321,不然不满足123一次进栈的要求,所以此时不会发生1比2先出栈的情况

栈的基本操作

InitStack(&S):初始化一个空栈S
StackEmpty(S):判断一个栈是否为空,若栈S为空返回true,否则返回false
Push(&S,x):进栈,若栈S未满,将x加入使之成为新栈顶
Pop(&S,&x):出栈,若栈S非空,弹出栈顶元素,并用x返回
GetTop(S,&x):读栈顶元素,若栈S非空,用x返回栈顶元素
ClearStack(&S):销毁栈,并释放栈S占用的存储空间。

(符号“&”是C艹特有的,用来宝石引用,有的采用C语言中的指针类型“*”,也可以达到传址的目的)

如果没有特殊要求,可以直接用这些基本的操作函数

栈的顺序存储结构(顺序栈)

顺序栈的实现

栈的顺序存储称为顺序栈,它是利用一组地址连续的存储单元存放自栈底到栈顶的数据元素,同时附设一个指针(top)指示当前栈顶的位置。

顺序栈可以描述为:

#define MaxSize 50              //定义栈中元素的最大个数
typedef struct{
    Elemtype data[MaxSize];     //存放栈中元素
    int top;                    //栈顶指针
}SqStack;

共享栈

利用栈底位置相对不变的特性,可以让两个顺序栈共享一个一位数据空间,将两个栈的栈底分别设置在共享空间的两端,两个栈向共享空间的中间延伸。

两个栈的栈顶指针都指向栈顶元素,top0=-1时0号栈为空,top1=MaxSize时1号栈为空,当且仅当两个栈顶的指针相邻时,判断为占满。当0号栈进栈时top0先加1再复制,1号栈进栈时top1先减1再复制;出栈时刚好相反。

共享栈是为了更有效地利用存储空间,两个栈的空间相互调节,只有在整个存储空间被占满时才发生上溢。其存取数据的时间复杂度均为O(1)所以对存取效率没有什么影响。

事实上使用遮掩的数据结构,通常都是当两个栈的空间需求有相反关系时,也就是一个栈增长时另一个栈在缩短的情况。就像买卖股票一样,你买入时,一定是有一个你不知道的人在做卖出操作。有人赚钱,就一定有人赔钱。这样使用两栈共享空间存储方法再有比较大的意义。

当然,这里指两个具有相同数据类型的栈,类型不同不适用这种方法。

栈的链式存储结构(链栈)

采用链式存储的栈成为链栈,链栈的优点是便于多个栈共享存储空间和提高效率,且不存在在栈满上溢的情况。通常采用单链表实现,并规定所有操作都是在单链表的表头进行的。这里规定链栈没有头结点,Lhead指向栈顶元素。

栈的链式存储类型可以描述为

typedef struct Linknode{
    ElemType data;          //数据域
    struct Linknode *next;  //指针域
} *LinkStack;               //栈类型定义

习题部分

选择题

第一题

栈和队列具有相同的()
A. 抽象数据类型 B. 逻辑结构 C. 存储结构 D. 运算

第二题

设链表不带头结点且所有操作均在表头进行,则下列最不是和作为链栈的是()
A. 只有表头结点指针,没有表尾指针的双向循环链表
B. 只有表尾结点指针,没有表头指针的双向循环链表
C. 只有表头结点指针,没有表尾指针的单向循环链表
D. 只有表尾结点指针,没有表头指针的单向循环链表

第三题

向一个栈顶指针为top的链栈中插入一个x结点,则执行()
A. top->next=x B. x->next=top->next;top->next=x

C. x-next=top;top=x D. x->top->data;top=top->next

第四题

链栈执行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

第五题

设栈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

第六题

若已知一个栈的入栈顺序是1、2、3、4.其出栈序列为P1、P2、P3、P4、,则P2、P4不可能是()

A. 2、4 B. 2、 1 C. 4 、 3 D. 3 、 4


解答部分

第一题

线性表、栈和队列的逻辑结构都是相同的,都属于线性结构,只是它们对数据的运算不同,从而表现出不同的特点

第二题

通常栈的插入和删除在表头进行。对于选项C,插入和删除一个结点后,仍需要将其变为循环单链表,因此需要找到其尾结点,时间复杂度为O(n)。

若不做题干中的限制,则栈顶可取表头(带头结点的链表)或第二个结点(不带头结点的链表),栈指针的位置取投机诶单(带头结点的链表)或表头(不带头结点的链表)

第三题

链栈采用不带头结点的单链表表示时,进栈操作在首部插入一个结点x(即x->next=top),插入完后需要将top指向该插入的结点x。如果带头结点呢?

第四题

这里假设栈顶指针指向的是栈顶元素,所以选D;而A中首先将top指针赋给了x,错误;B中没有修改top指针的值;C为top指针指向栈顶元素的上一个元素时的答案。

第五题

本题将队列和栈结合起来,由于队列“先进先出”的特性,所以出队的序列与进队的序列是相通的,从而可以得到出栈的次序为2,4,3,6,5,1;再由栈“先进后出”的特性,进栈的次序为1,2,3,4,5,6,可见排在某个元素之后出栈却在它会前进栈的元素个数,表示之前栈内元素个数。因此,4进栈后,1和3在栈中;而后4和3出栈;6进栈后,5和1也在栈中。因此,栈的最小容量是3。

第六题

对于A,可能的顺序是1入栈,1出栈,2入栈,2出栈,3入栈,3出栈,4入栈,4出栈。
对于B可能的顺序是1234入栈,4321出栈
D可能的顺序是1入栈,1出栈,2入栈,3入栈,3出栈,2出栈,4入栈,4出栈。
而C却没有对应的序列。

综合题

习题部分

第一题

假设以I和O 分别表示入栈和出栈操作。栈的初态和终态均为空,入栈和出栈的操作序列可表示为仅由I和O组成的序列,可以操作的序列称为合法序列,否则称为非法序列。

1)下面所示的序列中哪些是合法的?

A. IOIIOIOO B. IOOIOIIO C. IIIOIOIO D. IIIOOIOO

2)通过对1)的分析,写出一个算法,判定所给的操作序列是否合法。若合法,返回true否则返回false(假定被判定的操作序列已存入一维数组中)

第二题

设单链表的表头指针为L,结点结构由data和next两个域构成,其中data域为字符型。试设计算法判断该链表的全部n个字符是否中心对称。例如xyx、xyyx都是中心对称

第三题

设有两个栈s1、s2都采用顺序栈方式,并且共享一个存储区[0,…,maxsize-1],为了尽量利用空间,减少溢出的可能,可采用栈顶相向,迎面增长的存储方式。试设计s1、s2有关入栈和出栈的操作算法

解答部分

第一题

1) AD
2)设被判定的操作序列已存入一维数组A中。

算法的基本设计思想:一次逐一扫描入栈出栈序列(即I和O组成的字符串)每扫描至任一位置均需检查出战次数(即O的个数)是否小于入栈次数(I的个数),若大于则为非法序列。扫描结束后,再判断入栈和出栈数是否相等,若不相等则不合题意,为非法序列。

int Judge(char A[]){
    //判断字符数组A中的输入输出序列是否是和发序列。如果是返回true,否则返回false
    int i =0;
    int 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’或‘O’,指针i均后移
    }                                   //while
    if(j!=k){
        printf("序列非法\n");
        return false;
    }
    else{
        printf("序列合法\n");
        return true;
    }
}

另解:入栈后,栈内元素个数加1;出栈后,栈内元素个数减1,因此,可以将判定一组出入栈序列是否合法,转化为一组+1、-1组成的序列,它的任一前缀子序列的累加和不小于0(每次出栈或入栈操作后判断),则合法;否则,非法

第二题

算法思想:使用栈来判断链表中的数据是否中心对称。将链表的前一半元素一次进栈。在处理链表的后一半元素时,当访问到链表的一个元素后,就从栈中弹出一一个元素,两个元素比较,若相等,则将链表中下一个元素与栈中再弹出的元素比较,直至链表到尾。这时若栈是空栈,则得出链表中心对称的结论;否则,当链表中的一个元素与栈中弹出元素不等时,结论为链表非中心对称,结束算法的执行

int dc(LinkList L,int n){
    //L是带头结点的n个元素单链表,本算法判断你链表是否失中心对称
    int i;
    char s[n/2];
    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循环,不再进行比较。

第三题

两个栈共享向量空间,将两个栈的栈底设在向量两段,初始时,s1栈顶指针为-1,s2栈顶指针为maxsize。两个栈顶指针相邻时为栈满。两个栈顶相向、迎面增长,栈顶指针指向栈顶元素。

#define maxsize 100             //两个栈共享顺序存储空间所能达到的最多元素数初始化为100

#define elemtp int              //假设元素类型为整型
typedef struct{                 
    elemtp stack[maxsize]       //栈空间
    int top[2];                 //top为两个栈顶指针
}stk;

本题的关键在于,两个栈入栈和退栈时的栈顶指针的计算。s1栈时通常意义下的栈;而s2栈入栈操作时,其栈顶指针左移(-1),退栈时,栈顶指针右移(+1)

此外,对于所有栈的操作,都要注意“入栈判满,出栈判空”的检查

(1)

int push(int i ,elemtp x){
    //入栈操作,i为栈号,i=0表示左边的s1栈,i=1表示右边的s2栈,x是入栈元素
    //入栈成功返回1,否则返回0

    if(i<0||i>1){
        printf("栈号输入不对\n");
        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=1时为s2栈
    //退栈成功返回退栈元素,否则返回-1
    if(i<0||i>1){
        printf("栈号输入不对\n");
        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
}

猜你喜欢

转载自blog.csdn.net/qq_22073849/article/details/78355247
今日推荐