栈和队列的经典例题,LeetCode 括号匹配问题;栈实现队列;队列实现栈;队列带环问题

1.前序

又有很久没有更新文章了,这次带你们手撕几道基础题;真的就和康纳吃饭一样简单!!!

如果还不会队列和栈的可以去看看之前写的博客; 栈的实现       队列概念以及实现  <- 快速传送

目录

1.前序

2.括号匹配问题,有效的括号 <- 快速传送

3.用队列实现栈   <- 快速传送

3.1 核心思路

3.2代码部分,上面是思路

4.用栈实现队列  <- 快速跳转

4.1这里用c语言实现的所以要复制一个栈

5.带环队列 <- 快速跳转


2.括号匹配问题,有效的括号 <- 快速传送

  1. 核心思路:
    1. 一定是左括号入栈,右括号在外面
    2. 入完数据尝试匹配,不相等 可以,如果相等可以走到最后返回true
    3. 左括号有数据;右括号没数据;此时说明返回false
    4. 右括号有数据;左括号没有;右括号还有没匹配的,此时*s有数据进了循环,if判断栈为NULL此时为false

  1. 根据上图分析,有4种情况
  2. 第一步,建立循环有数据就往下读取,左括号入栈,右括号不如栈,进入的数据拿去比对
  3. 先处理正常情况,没有进入到if判断(不匹配的情况),此时就可以证明时匹配上了,因为没有返回false
  4. 注意:不匹配的情况;当满足左括号和右括号对应的时候,此时就可以不会进if
bool isValid(char* s) {
    ST str;
    STInit(&str);
    while(*s)//有数据就继续读
    {
        if(*s == '(' || *s == '{' || *s == '[')//是左括号就入栈
        {
            STPush(&str,*s);
        }
        else//尝试和右括号匹配
        {
            //为 0 时返回 true,然后执行if,返回false,匹配失败
            if(STEmpty(&str))//经过前面的匹配后,此时栈为NULL了,但是*s 还有数据
            {
                STDestroy(&str);
                return false;
            }
            char top = STTop(&str);//不管取没取到,先Pop,如果匹配成功就正常往下走
            STPop(&str);
            //不匹配的情况,不匹配直接返回
            if(top == '(' && *s != ')' ||
               top == '[' && *s != ']' ||
               top == '{' && *s != '}')
            {
                //销毁空间,并返回
                STDestroy(&str);
                return false;
            }
        }
        s++;//找下一个元素
    }
    //返回值为false说明有栈内有数据,但是前面while已经结束,没有字符可以匹配了
    //意味着左括号比右括号的多 ,只要不匹配就返回false
    bool ret = STEmpty(&str);
    STDestroy(&str);
    return ret;//正常返回
}

3.用队列实现栈   <- 快速传送

  1. 传参部分要注意,这个MyStack结构体里装着两个队列
  2. 这里要传&(que->q1)或者&que->q1,因为这是两个结构体,改变结构体需要结构体指针
  3. Create部分,首先需要给MyStack(匿名结构体)开辟空间,这里他让我们初始化两个队列,并且完成初始化,这部分就直接调用函数即可。

3.1 核心思路

核心思路:插入数据时始终插入到有数据的队列去;始终要空出一个队列,用来删除

  1. Push,实现栈的后进后出,第一次开始数据随便放,后面插入数据要放到有数据后
    1. QueueEmpty,这个函数为NULL返回true(表示没有数据),反之返回false,说明此时有数据;
    2. 注意:!取反,将结果取反,此代码中有数据返回false取反后得到true
  2. Pop,这里利用假设法;先假设q1有数据,假设失败那就是q2,这里的if判断同样用判空来判断是否有数据
    1. 这里while里面就是挪动数据了 ,此时你需要取到头元素,挪动size - 1个数据,留一个在que1里面pop掉,就模拟了栈的出栈
    2. QueuePop(NoEmpty);//只是被导到Empty,原空间还有数据,所以要Pop掉已经导过去的数据
    3. 这里根据题目要求,要保存数据,然后要pop掉这块空间,最后返回该值

  1. Top,取栈的top元素,也就是队列的尾元素,当然这个尾元素也是需要看,数据在哪一边,同样的配方,判NULL
  2. Empty栈的判NULL,竟然是由两个队列组成自然也就需要,两个都为NULL才行,&& 也是运算符;同时为NULL,返回true
  3. 这里的free,要了解结构体,开始初始化是什么,你就怎么销毁;
  4. 这里销毁时候先后顺序的,不能直接销毁obj,而是要先销毁obj里面两个的q1和q2才行

3.2代码部分,上面是思路

typedef struct {
    Queue q1;
    Queue q2;
} MyStack;

MyStack* myStackCreate() {
    MyStack* que = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&(que->q1));//通过结构体指针访问到q1,用初始化函数直接初始化
    QueueInit(&(que->q2));
    return que;
}

void myStackPush(MyStack* obj, int x) {//obj 是装着q1 和 q2两个结构体的
    //分为两种情况,那个后面有数据有往哪里放,都没有随便放一个
    if(!QueueEmpty(&(obj->q1)))//如果q1这个队列有数据,返回false,! 取反后true,执行if
    {
        QueuePush(&(obj->q1),x);
    }
    //obj->q2有数据,或者都没有
    else
    {
        QueuePush(&(obj->q2),x);
    }
}
int myStackPop(MyStack* obj) {
    //假设法,因为当一方成立是,双方代码一样
    Queue* Empty = &(obj->q1);//假设p1没数据 
    Queue* NoEmpty = &(obj->q2);//有数据
    if(!QueueEmpty(&(obj->q1)))//非NULL,有数据;假设失败
    {
        Empty = &(obj->q2);
        NoEmpty = &(obj->q1);
    }
    while(QueueSize(NoEmpty) > 1)//有数据的一方需要导到无数据的一方,个数size - 1
    {
        QueuePush(Empty,QueueFront(NoEmpty));//取到有数据的队头,导到无数据的一方
        QueuePop(NoEmpty);//只是被导到Empty,原空间还有数据,所以要Pop掉已经导过去的数据
    }
    QuDataType ret = QueueFront(NoEmpty);
    QueuePop(NoEmpty);
    return ret;
}

int myStackTop(MyStack* obj) {
    if(!QueueEmpty(&(obj->q1)))//此时q1有数据,直接取到队列的队尾
    {
        return QueueBack(&(obj->q1));
    }
    else
    {
        return QueueBack(&(obj->q2));
    }
}

bool myStackEmpty(MyStack* obj) {
//只有这两个队列同时为NULL,才说明没数据了;满足两个判空都为NULL,才会返回true,&&也相当于是运算符
    return QueueEmpty(&(obj->q1)) && QueueEmpty(&(obj->q2));
}

void myStackFree(MyStack* obj) {
    QueueDestroy(&(obj->q1));//首先要free掉q1和q2 这两个队列,然后才是obj
    QueueDestroy(&(obj->q2));
    free(obj);
}

4.用栈实现队列  <- 快速跳转

  1. 此题创建两个栈来实现,stpush只入数据,相当于入队;stpop只出数据,相当于出队
  2. Create,首先是初始化MyQueue 里面放上两个栈,栈也初始化;
  3. Push;push数据那么调用栈的push函数即可;注意:STPush只用来放数据
  4. Peek,返回开头元素;把数据都导到stpop这样刚好压栈的元素就在栈顶;
    1. 要让这里的stpop没有数据才能导入进去只要stpop有数据就不要进
    2. 因为要始终保持队列的先进先出,假如stpush的数据导过来不就乱了
    3. 如果没进if说明有数据,那就不要导到stpop了
  5. 这里用动图说明原因;只有stpop数据出完才进数据

  1. Pop,删除队列开头的数据并返回元素;这里为什么调用一下myQueuePeek函数呢?
    1. 因为我们拿数据始终在stpop拿;那么stpop没数据自然要!!!
    2. 有了数据直接取头元素,删除返回即可
  2. Empty;判断是为NULL,因为这个是模拟是队列的功能;所以两个都要为NULL
  3. Free;怎么申请的空间,怎么倒着销毁;和队列实现栈的销毁逻辑一样

4.1这里用c语言实现的所以要复制一个栈

typedef struct {
    ST stpush;//入队
    ST stpop;//出队
} MyQueue;

MyQueue* myQueueCreate() {
    MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));
    STInit(&(obj->stpush));
    STInit(&(obj->stpop));

    return obj;
}

void myQueuePush(MyQueue* obj, int x) {
    STPush(&(obj->stpush),x);//stpush只入数据
}

int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&(obj->stpop)))//pop这边栈没数据就要导入数据
    {
        while(!STEmpty(&(obj->stpush)))//push有数据就到导
        {
            int top = STTop(&(obj->stpush));
            STPush(&(obj->stpop),top);
            STPop(&(obj->stpush));//数据挪到那边就要删除了
        }
    }
    return STTop(&(obj->stpop));
}
int myQueuePop(MyQueue* obj) {
    int front = myQueuePeek(obj);//stpop这边有数据才能删,如果不用调整刚好也拿到了
    STPop(&(obj->stpop));
    return front;
}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&(obj->stpush)) && STEmpty(&(obj->stpop));//两个都为NULL才为NULL
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&(obj->stpush));
    STDestroy(&(obj->stpop));
    free(obj);
}

5.带环队列 <- 快速跳转

  1. Create,这个部分就是初始化了,需要开辟两个空间,一个是MyCircularQueue结构体的另外一个就是arr 数组的
  2. IsEmpty,判NULL

  1. 多开一块空间的意义是,防止head ==tail时,不知道是满还是NULL
  2. IsFull,判满

  1. EnQueue,向循环队列插入一个元素;在开始之前肯定要判断是否为满,满了就不能插入了

  1. DeQueue,从循环队列中删除一个元素;在开始之前要判NULL,不然你删个屁

  1. Front;开始之前肯定要判NULL;head位置的数据没啥问题直接取到就行,特殊情况在tail,因为指向下一个
  2. Rear,取尾的数据;普通情况正常取;特殊情况就是有效数据正好是最后一个,当tail指向下一个位置(也就是下标0);

typedef struct {
    int* arr;
    int head;
    int tail;
    int k;
} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* Cqueue = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    if(Cqueue == NULL)
    {
        perror("malloc Cqueuq");
        return 0;
    }
    //多开辟一个空间防止假溢出,这里不需要临时变量,也和数据丢不丢是没关系
    Cqueue->arr = (int*)malloc(sizeof(int) * (k + 1));
    Cqueue->head = Cqueue->tail = 0;//刚开始都指向下标0
    Cqueue->k = k;
    return Cqueue;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->head == obj->tail;//判断当前存储的位置是否为NULL
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return ((obj->tail + 1) % (obj->k + 1)) == obj->head;//满了返回true
}

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

    obj->arr[obj->tail] = value;
    obj->tail++;
    obj->tail %= (obj->k + 1);
    return true;  
}

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

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

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->arr[obj->head];//取当前数据
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->arr[(obj->tail - 1 + obj->k + 1) % (obj->k + 1)];
}

void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->arr);
    free(obj);
}
  1. free,空间的销毁就是,怎么申请怎么销毁;假如先申请的obj后申请的obj->arr;那么就反着销毁

总结;

  1. 最近改变了一下学习方式;上完课要及时写作业,而且要独立完成效果最好
  2. 反正都这么菜了,多进步一点也算好的;总之不能止步不前

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/642555.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

HTTP响应的基本概念

目录 HTTP响应中的一些信息 HTTPS HTTP响应中的一些信息 状态码&#xff1a;描述了这次HTTP请求是否成功&#xff0c;以及失败的原因。 1&#xff09;200 ---OK 表示这次访问成功了。 2&#xff09;404 ---Not Found 表示客户端请求的资源在服务器这边不存在。 3&a…

线性表(从数据结构的三要素出发)

文章目录 逻辑结构存储结构顺序存储链式存储单链表双链表循环单链表循环双链表静态链表 数据的操作顺序结构链式结构单链表双链表 逻辑结构 线性表是具有相同数据类型的 n ( n ≥ 0 ) n(n≥0) n(n≥0)个数据元素的有限序列&#xff0c;其中 n n n为表长&#xff0c;当 n 0 n0…

KMP算法【C++】

KMP算法测试 KMP 算法详解 根据解释写出对应的C代码进行测试&#xff0c;也可以再整理成一个函数 #include <iostream> #include <vector>class KMP { private:std::string m_pat;//被匹配的字符串std::vector<std::vector<int>> m_dp;//状态二维数组…

WXSS模板样式-全局样式和局部样式

一、WXSS 1.WXSS WXSS(WeiXin Style Sheets)是一套样式语言&#xff0c;用于美化WXML的组件样式&#xff0c;类似于网页开发中的CSS 2.WXSS和CSS的关系 WXSS具有CSS大部分特性&#xff0c;同时&#xff0c;WXSS还对CSS进行了扩充以及修改&#xff0c;以适应微信小程序的开发…

NetSuite Intercompany COGS科目设置问题

在22年底的NetSuite多公司功能串讲中&#xff0c;有一个题目是Intercompany COGS科目的设置问题。近期在项目上这个问题被密集讨论。为了方便分享&#xff0c;所以在此摘出来独立成文。有兴趣的同学也可以翻看之前的视频。 NetSuite知识会 第8谈 多公司功能串讲 NetSuite Inter…

Spring6基础笔记

Spring6 Log4j2 1、概述 1.1、Spring是什么&#xff1f; Spring 是一款主流的 Java EE 轻量级开源框架 &#xff0c;Spring 由“Spring 之父”Rod Johnson 提出并创立&#xff0c;其目的是用于简化 Java 企业级应用的开发难度和开发周期。Spring的用途不仅限于服务器端的开发…

MPLS LDP原理与配置

1.LDP基本概念 &#xff08;1&#xff09;LDP协议概述 &#xff08;2&#xff09;LDP会话、LDP邻接体、LDP对等体 &#xff08;3&#xff09;LSR ID 与LDP ID &#xff08;4&#xff09;LDP消息 ⦁ 按照消息的功能&#xff0c;LDP消息一共可以分为四大类型&#xff1a;发现…

【C++STL详解(四)------vector的模拟实现】

文章目录 vector各函数接口总览vector当中的成员变量介绍默认成员函数构造函数1构造函数2构造函数3拷贝构造函数赋值运算符重载函数析构函数 迭代器相关函数begin和end 容量和大小相关函数size和capacityreserveresizeempty 修改容器内容相关函数push_backpop_backinserterases…

OpenStack平台Keystone组件的使用

1. 规划节点 安装基础服务的服务器规划 IP地址 主机名 节点 192.168.100.10 controller Openstack控制节点 2. 基础准备 使用机电云共享的单节点的openstack系统&#xff0c;自行修改虚拟网络编辑器、网络适配器&#xff0c;系统用户名&#xff1a;root&#xff0c;密…

【数据分析面试】53.推送消息的分布情况(SQL)

题目 我们有两个表&#xff0c;一个是 notification_deliveries 表&#xff0c;另一个是包含 created 和购买 conversion dates 的 users 表。如果用户没有购买&#xff0c;那么 conversion_date 列为 NULL。 编写一个查询&#xff0c;以获取用户转换前的推送通知总数的分布情…

51 单片机[4]:数码管显示

目标&#xff1a; 一次显示一个数字&#xff1a;在数码管第三位显示6.同时显示多个不同数字&#xff1a;在数码管前三位分别显示1, 2, 3. 一、认识数码管 LED数码管&#xff1a;数码管是一种简单、廉价的显示器&#xff0c;是由多个发光二极管封装在一起组成“8”字型的器件…

零拷贝(Zero-Copy)

1.背景 现在有这样一个场景&#xff0c;我们需要在本地选择一个文件后&#xff0c;然后上传到网络上。 我们再看看文件的内容数据的具体搬运过程&#xff1a; 你会发现&#xff0c;在整个文件搬运的过程中&#xff0c;发生了多次的数据拷贝和上下文转换。 4次数据拷贝&#…

amis 联动效果触发的几种方式

联动效果实现主要俩种方式: 1.表达式实现联动,基于组件内或数据链的变量变化的联动 比如&#xff1a; "source": "/amis/api/mock2/options/level2?name${name} " (必须是这种字符串拼接形式,在data数据映射中表达式不会触发联动) 所有初始化接口链…

【Linux】中的常见的重要指令(中)

目录 一、man指令 二、cp指令 三、cat指令 四、mv指令 五、more指令 六、less指令 七、head指令 八、tail指令 一、man指令 Linux的命令有很多参数&#xff0c;我们不可能全记住&#xff0c;我们可以通过查看联机手册获取帮助。访问Linux手册页的命令是 man 语法: m…

【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理,以及遇到的困难和总结

&#x1f493; 博客主页&#xff1a;从零开始的-CodeNinja之路 ⏩ 收录文章&#xff1a;【Spring Boot】深度复盘在开发搜索引擎项目中重难点的整理&#xff0c;以及遇到的困难和总结 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 目录 什么是搜索引…

基于SpringBoot+Vue的人事管理系统

引言 目前,人事管理的系统大都是CS架构的大型系统,很少有面向机关,事业单位内部的基于BS架构的微型人事系统,因此.开发一个基于BS架构的人事信息管理系统是非常必要的.但是基于BS架构的人事系统对于安全是一个大的考验点.在人事信息系统中,功能需简单清晰,可操作性强,其次安全…

站在ESG“20+”新起点上,看中国ESG先锋探索力量

全链减碳、建设绿色工厂、打造零碳产品、守护生物多样性、向受灾群众捐助……不知你是否察觉&#xff0c;自“双碳”目标提出以来&#xff0c;一股“可持续发展热潮”正覆盖各行各业&#xff0c;并且渗透到我们衣食住行的方方面面。在资本市场&#xff0c;ESG投资热潮更是席卷全…

外汇天眼:风险预警!以下平台监管牌照被撤销!

监管信息早知道&#xff01;外汇天眼将每周定期公布监管牌照状态发生变化的交易商&#xff0c;以供投资者参考&#xff0c;规避投资风险。如果平台天眼评分过高&#xff0c;建议投资者谨慎选择&#xff0c;因为在外汇天眼评分高不代表平台没问题&#xff01; 以下是监管牌照发生…

Leetcode | 5-21| 每日一题

2769. 找出最大的可达成数字 考点: 暴力 数学式子计算 思维 题解 通过式子推导: 第一想法是二分确定区间在区间内进行查找是否符合条件的, 本题最关键的便是 条件确定 , 第二种方法: 一般是通过数学公式推导的,这种题目我称为数学式编程题 代码 条件判断式 class Solution { …

ViT:1 从DETR说起

大模型技术论文不断&#xff0c;每个月总会新增上千篇。本专栏精选论文重点解读&#xff0c;主题还是围绕着行业实践和工程量产。若在某个环节出现卡点&#xff0c;可以回到大模型必备腔调重新阅读。而最新科技&#xff08;Mamba,xLSTM,KAN&#xff09;则提供了大模型领域最新技…