> 文档中心 > 冰冰学习笔记:这些栈与队列的练习题,你会吗?

冰冰学习笔记:这些栈与队列的练习题,你会吗?


 欢迎各位大佬光临本文章!!!

还请各位大佬提出宝贵的意见,如发现文章错误请联系冰冰,冰冰一定会虚心接受,及时改正。

本系列文章为冰冰学习编程的学习笔记,如果对您也有帮助,还请各位大佬、帅哥、美女点点支持,您的每一分关心都是我坚持的动力。

我的博客地址:bingbing~bang的博客_CSDN博客https://blog.csdn.net/bingbing_bang?type=bloghttps://blog.csdn.net/bingbing_bang?type=blog

我的gitee:冰冰棒 (bingbingsupercool) - Gitee.https://gitee.com/bingbingsurercool


系列文章推荐

冰冰学习笔记:这些链表练习题,你会吗?(上)

冰冰学习笔记:这些链表练习题,你会吗?(中)

冰冰学习笔记:这些链表练习题,你会吗?(下)


目录

系列文章推荐

前言

1.有效的括号

使用栈来实现

2.用栈实现队列

3.用队列实现栈

总结


前言

        学习完栈与队列后,我们要将知识融会贯通,充分发挥这两种数据结构的优点,下面是几道使用栈和队列来完成的练习题。学会这些练习题会对栈与队列的认识更加深刻,使用也会逐渐变得得心应手。

1.有效的括号

题目链接:20. 有效的括号 - 力扣(LeetCode)

题目描述:给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

使用栈来实现

        什么意思呢?该题目就是给你一串只包含上述三种括号类型的字符串,判断字符串中的所有括号是否都一一对应,即所有的左括号 '(' 是否正好匹配所有的右括号 ')' 。

        但是我们要知道,就算数目对的上也不一定就是匹配的。例如出现下面两种情况:

        此种情况下虽然数目是对的,但是方向并不对,也就是说当遇到 左括号 '(' 时,下一个右括号必须为 ')' 否则就不能匹配成功。 

        那该如何解答呢?这里我们就要使用栈的特点了,栈的数据存储特点是先进后出。因此我们可以这样解答:在遇到左括号的时候进行入栈操作,在遇到右括号的时候进行出栈操作,然后判断此时字符串中的括号与出栈的括号是否匹配,如果匹配就继续向后移动,不匹配直接返回false,直到遍历完整个字符串。

        基于上面的分析,我们的代码实现就比较简单了。首先我们将自己写好的栈复制到代码中,然后开始构建逻辑代码。代码分析如下图所示:

        两个蓝色框圈起来的为特殊情况处理,如果一开始就遇到右括号,此时如果直接对其进行出栈操作,由于栈内部并没有数据,因此必然会导致程序错误,所以在出栈之前对其栈进行判空操作,如果为空则返回false,证明直接遇到了右括号,为错误形式。不为空在取出进行对比。

        当循环遍历结束,我们并不能直接证明是否为正确形式,此时还需要对栈进行判空处理,栈为空,说明所有的括号匹配完毕,如果不为空,则说明有多余的左括号,所以也是错误形式。我们保存ret为判空的结果,栈为空ret中为true,返回的也是true,栈不为空,ret为false,返回的也是false。

实现代码:

//判断括号匹配问题bool isValid(char* s){    ST st;    StackInit(&st);    while ( *s )    { if ( *s == '(' || *s == '[' || *s == '{' ) {     StackPush(&st, *s);     s++; } else {     if ( StackEmpty(&st) )     {  StackDestroy(&st);  return false;     }     else     {  char top = StackTop(&st);  StackPop(&st);  if ( (top == '(' && *s == ')')      || (top == '{' && *s == '}')      || (top == '[' && *s == ']') )      s++;  else  {      StackDestroy(&st);      return false;  }     } }    }    bool ret = StackEmpty(&st);    StackDestroy(&st);    return ret;}

2.用栈实现队列

题目链接:232. 用栈实现队列 - 力扣(LeetCode)

题目描述:使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(pushpoppeekempty)

        该题目的意思是让我们使用两个栈来模拟实现一个队列,栈的性质是后进先出,而队列的性质是先进后出。因此,我们可以采用两个栈进行数据轮转来进出。

        设计两个栈push和pop,一个栈负责存储数据,一个栈负责弹出数据。由于栈具有先进后出的性质,因此我们将数据存入到push栈中,例如存入数据1,2,3。如果直接出栈,出栈顺序为3,2,1。并不符合队列的出栈顺序1,2,3。那如果我们在调用出队函数时,将push栈的数据先弹出导入到pop栈中,数据从pop栈中弹出,此时栈push以3,2,1的顺序将数据放入到pop栈中,pop栈的进栈顺序为3,2,1。然后进行出栈操作,此时的出栈顺序为1,2,3。符合了队列先进先出的原则。

        出队的时候我们要先判断pop栈是否为空,不为空则直接将数据从pop栈中弹出,如果为空,我们需要将push栈中的数据先导入pop栈,然后在完成出队操作。返回队顶元素,则直接返回pop栈不为空时的栈顶元素即可。队列为空则表明栈push与栈pop都为空。销毁队列,则需要先销毁两个栈的空间在销毁队列的空间。具体代码如下所示:

typedef struct{    ST pushst;    ST popst;} MyQueue;MyQueue* myQueueCreate(){    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));    StackInit(&obj->pushst);    StackInit(&obj->popst);    return obj;}void myQueuePush(MyQueue* obj, int x){    StackPush(&obj->pushst, x);}int myQueuePop(MyQueue* obj){    if ( StackEmpty(&obj->popst) )    { while ( !StackEmpty(&obj->pushst) ) {     StackPush(&obj->popst, StackTop(&obj->pushst));     StackPop(&obj->pushst); }    }    int top = StackTop(&obj->popst);    StackPop(&obj->popst);    return top;}int myQueuePeek(MyQueue* obj){    if ( StackEmpty(&obj->popst) )    { while ( !StackEmpty(&obj->pushst) ) {     StackPush(&obj->popst, StackTop(&obj->pushst));     StackPop(&obj->pushst); }    }    return StackTop(&obj->popst);}bool myQueueEmpty(MyQueue* obj){    return StackEmpty(&obj->pushst) && StackEmpty(&obj->popst);}void myQueueFree(MyQueue* obj){    StackDestroy(&obj->pushst);    StackDestroy(&obj->popst);    free(obj);}

3.用队列实现栈

题目链接:225. 用队列实现栈 - 力扣(LeetCode)

题目描述:使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(pushtoppop 和 empty)。

        该题目与上一个题目相似,只不过需要使用队列先进先出的性质去实现栈后进先出的性质。有了上一题的铺垫,我们很容易想到使用两个队列相互倒换数据来完成。

        首先创建两个队列q1,q2,使其组成栈的基本结构,然后进行初始化操作。在执行数据进栈功能时,先判断两个队列q1,q2是否为空,找到空的队列进行数据存放。在执行出队列操作时,我们需要先找到数据不为空的队列nonempty,然后将里面的n-1个数据倒换到另一个队列中,然后将队列nonempty的最后一个队列数据进行弹出操作。此时得到的就是最后进入队列的数据,从而满足了栈后进先出的操作。入栈出栈过程如下所示。

 

        此时再次操作入栈函数,函数又会重新在q1,q2中寻找空队列进行数据插入。对于栈顶元素获取,无非是数据入队后返回不为空的队尾的数据,判断栈为空则为两个队列都为空。栈空间销毁则为先销毁队列的空间,然后释放掉栈的空间,具体代码如下所示。

代码实现:

typedef struct {    Queue q1;    Queue q2;} MyStack;MyStack* myStackCreate() {    MyStack*ps=(MyStack*)malloc(sizeof(MyStack));    if(ps==NULL)    { perror("malloc"); exit(-1);    }    QueueInit(&ps->q1);    QueueInit(&ps->q2);    return ps;}void myStackPush(MyStack* obj, int x) {    if(!QueueEmpty(&obj->q1))    { QueuePush(&obj->q1,x);    }    else    { QueuePush(&obj->q2,x);    }}int myStackPop(MyStack* obj) {    Queue*emptyQ=&obj->q1;    Queue*nonemptyQ=&obj->q2;    if(!QueueEmpty(&obj->q1))    { emptyQ=&obj->q2; nonemptyQ=&obj->q1;    }    while(QueueSize(nonemptyQ)>1)    { QueuePush(emptyQ,QueueFront(nonemptyQ)); QueuePop(nonemptyQ);    }    QueueDataType top=QueueFront(nonemptyQ);    QueuePop(nonemptyQ);    return top;}int myStackTop(MyStack* obj) {     if(!QueueEmpty(&obj->q1))    {return QueueBack(&obj->q1);    }    else    {return  QueueBack(&obj->q2);    }}bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1)&&QueueEmpty(&obj->q2);}void myStackFree(MyStack* obj) {    QueueDestroy(&obj->q1);    QueueDestroy(&obj->q2);    free(obj);}

总结

        栈与队列的练习题总体来说并不难,只要深刻理解了栈与队列的性质就能将这些练习题完全搞懂,对于练习题来说,还有一种循环队列的实现,作者使用链表与数组两种方式进行了搭建,这里就不多做介绍,有兴趣的可以自行去仓库下载观看。

循环队列练习题代码:循环队列两种形式实现,链表与数组 · a1d212e · 冰冰棒/数据结构仓库 - Gitee.com