OJ题讲解——栈与队列

目录

一.有效的括号

1.问题描述

2.问题详解

3.代码

二.用队列实现栈

1.问题描述

2.问题详解

3.代码

三.用栈实现队列

1.问题描述

2.问题详解

3.代码

四.设计循环队列

1.问题描述

2.问题详解

3.代码


一.有效的括号

1.问题描述

OJ链接:. - 力扣(LeetCode)

2.问题详解

对于这道题,采用栈来解决

1.如果是左括号就入栈

2.如果是右括号,取栈顶元素与之匹配

2.1 当栈里面为空时,表明右括号多了,没有左括号与之匹配,返回false

2.2 由于匹配的情况有很多种,无法一一列全,于是我们来判断不匹配的情况,当左右括号不匹配就返回false

2.3 当所有的右括号匹配完后还要检查栈是否为空,可能还有左括号没有匹配

3.代码

注意;

本篇博客都是在c语言的环境下,当涉及到用栈和队列,需要用户自己先创建栈或队列

cpp 复制代码
bool isValid(char* s) {
    //1.左括号入栈
    //2.右括号与出栈顶的左括号匹配
    ST st;
    STInit(&st);
    while(*s)
    {
        //左括号入栈
        if(*s=='('||*s=='['||*s=='{')
        {
            STPush(&st,*s);
        }
        else//右括号与出栈顶的左括号匹配
        {
            if(STEmpty(&st))
            {
                STDestroy(&st);
                return false;
            }

            char top=STTop(&st);
            STPop(&st);
            
            //不匹配的情况·
            if((top=='('&&*s!=')')
            ||(top=='['&&*s!=']')
            ||(top=='{'&&*s!='}'))
            {
                STDestroy(&st);
                return false;
            }
        }
        s++;
    }
    //栈不为空,说明左括号比右括号多,不匹配
    bool ret=STEmpty(&st);
    STDestroy(&st);

    return ret;
}

二.用队列实现栈

1.问题描述

OJ链接:. - 力扣(LeetCode)

2.问题详解

1.先创建一个结构体,里面存储俩个队列

2.对于实现栈push,top,pop,empty四个操作中,最麻烦的是pop操作,因为队列pop的是队头元素,而栈pop的是栈顶元素,因此我们需要用到俩个队列,保持一个队列存数据,一个队列为空,pop数据需要通过空队列导一下,将前size-1个数据按顺序边导到空队列中边删除掉,将最后一个数据保存后再删除掉,这样就获取栈顶的数据并且pop成功

3.top操作可以直接返回非空队列的队尾元素

4.push操作直接导入不为空的队列中

5.empty操作需要对俩个队列都进行判空操作

3.代码

cpp 复制代码
typedef struct {
    Queue q1;
    Queue q2;
} MyStack;


MyStack* myStackCreate() {
    MyStack*pst=(MyStack*)malloc(sizeof(MyStack));
    QueueInit(&(pst->q1));
    QueueInit(&(pst->q2));

    return pst;
}

void myStackPush(MyStack* obj, int x) {
    //将数据导入不为空的那个队列
    if(!QueueEmpty(&(obj->q1)))
    {
        //栈顶插入刚好就是队尾插入
        QueuePush(&(obj->q1),x);
    }
    else
    {
        QueuePush(&(obj->q2),x);
    }
}

int myStackPop(MyStack* obj) {
    //假设法
    Queue*empty=&(obj->q1);
    Queue*nonempty=&(obj->q2);
    if(!QueueEmpty(&(obj->q1)))
    {
        empty=&(obj->q2);
        nonempty=&(obj->q1);
    }

    //将有数据的队列的前size-1个导到空队列
    while(QueueSize(nonempty)>1)
    {
        QueuePush(empty,Queuefront(nonempty));
        QueuePop(nonempty);
    }

    int top= Queuefront(nonempty);
    QueuePop(nonempty);

    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);
}

三.用栈实现队列

1.问题描述

OJ链接:. - 力扣(LeetCode)

2.问题详解

创建俩个栈,分别为pushst和popst,pushst用来插入数据,popst用来导数据,删除队头元素(注意:与上问题的俩个队列不同,用队列实现栈需要俩个队列相互导数据,保证一个队列始终为空,而这个问题不需要俩个栈相互导数据),如下图所示:

通过上面的三个步骤,我们可以看出,当popst栈中为空,又需要pop队头元素时,我们直接将pushst栈中的元素导导popst栈中,然后删除的栈顶元素就是队头元素,同时当pushst和popst中都有元素,又想删除队头元素时,我们不需要再将pushst栈中的元素导到popst栈中去,只需要删除popst栈顶元素就好了。因此,插入数据只需要插入到pushst中,而删除队头数据,只有当popst栈中没有元素时,才需要将pushst中的元素全部导入popst中去,再删除栈顶元素

3.代码

cpp 复制代码
typedef struct {
    ST pushst;//用来输入数据
    ST popst;//用来倒数据,实现删除
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue*obj=(MyQueue*)malloc(sizeof(MyQueue));

    STInit(&(obj->pushst));
    STInit(&(obj->popst));

    return obj;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&(obj->pushst),x);
}

int myQueuePop(MyQueue* obj) {
    int top=myQueuePeek(obj);
    STPop(&(obj->popst));

    return top;
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&(obj->popst)))
    {
        //倒数据
        while(!STEmpty(&(obj->pushst)))
        {
            int top=STTop(&(obj->pushst));
            STPush(&(obj->popst),top);
            STPop(&(obj->pushst));
        }
    }

    return STTop(&(obj->popst));
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&(obj->pushst))&&STEmpty(&(obj->popst));
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&(obj->pushst));
    STDestroy(&(obj->popst));

    free(obj);
}

四.设计循环队列

1.问题描述

OJ链接:. - 力扣(LeetCode)

2.问题详解

循环队列是一种空间大小固定,空间能够反复利用的队列,这个队列也可以采用链表和数组来实现。

首先分析链表,用链表的优势在于它可以实现循环,但是创建链表要比队列大小多开一个节点,避免假溢出问题,因为head和tail初始化为头节点,tail实际表示的是最后一个数据指向的下一个节点。而链表的难点在于尾节点的获取,如果是单链表就需要从头开始遍历节点,增加了时间复杂度,或者再定义一个指针指向tail前一个节点,如果是双向链表恰好可以解决这个问题。
如果用数组来实现的话,当head和tail都初始化为0,我们来看看这个式子head==tail表示的是什么?好像表示队列满了又好像队列为空?这意思不矛盾了吗!对于解决这个有俩个办法,一是再定义一个size记录队列中元素的个数和队列空间大小进行比较,二是再开一个空间,当head==tail为空,当(tail+1)%(k+1)==head为满,而在本题,采用的是第二种解法,额外开辟一个空间。

注意:

插入和删除数据时,要确保这个队列是循环的,我们一定要tail=tail%(k+1),同时,在获取队尾元素时,也要时刻注意tail,确保能够形成一个循环队列

3.代码

cpp 复制代码
typedef struct {
    int* a;
    int head;//指向头
    int tail;//指向尾的下一个位置
    int k;
} MyCircularQueue;

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->head==obj->tail;
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->tail+1)%(obj->k+1)==obj->head;
}

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue*obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    //多开一个解决假溢出
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->head=0;
    obj->tail=0;
    obj->k=k;

    return obj;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    {
     return false;
    }

    obj->a[obj->tail]=value;
    obj->tail++;

    obj->tail%=(obj->k+1);

    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return false;

    obj->head++;
    obj->head%=(obj->k+1);

    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[obj->head];
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;

    int real=obj->tail==0?obj->k:obj->tail-1;
    return obj->a[real];

   //return obj->a[(obj->tail+obj->k)%(obj->k+1)];
}


void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}
相关推荐
古希腊掌管学习的神16 分钟前
[搜广推]王树森推荐系统笔记——曝光过滤 & Bloom Filter
算法·推荐算法
qystca17 分钟前
洛谷 P1706 全排列问题 C语言
算法
浊酒南街23 分钟前
决策树(理论知识1)
算法·决策树·机器学习
就爱学编程30 分钟前
重生之我在异世界学编程之C语言小项目:通讯录
c语言·开发语言·数据结构·算法
学术头条35 分钟前
清华、智谱团队:探索 RLHF 的 scaling laws
人工智能·深度学习·算法·机器学习·语言模型·计算语言学
Schwertlilien1 小时前
图像处理-Ch4-频率域处理
算法
北国无红豆1 小时前
【CAN总线】STM32的CAN外设
c语言·stm32·嵌入式硬件
IT猿手1 小时前
最新高性能多目标优化算法:多目标麋鹿优化算法(MOEHO)求解TP1-TP10及工程应用---盘式制动器设计,提供完整MATLAB代码
开发语言·深度学习·算法·机器学习·matlab·多目标算法
__lost1 小时前
MATLAB直接推导函数的导函数和积分形式(具体方法和用例)
数学·算法·matlab·微积分·高等数学
单片机学习之路1 小时前
【C语言】结构
c语言·开发语言·stm32·单片机·51单片机