栈与队列OJ题【括号适配问题】【用队列实现栈】【用栈实现队列】【设计循环队列】

 一.有效的括号

​​​OJ链接

 这一道题我们就可以用栈来解决:

不了解栈的可以看我的上一篇博客。

typedef char STDataType;
//用数组来实现栈
typedef struct stack
{STDataType* a;int capacity;int top;
}ST;
void STInit(ST* pst)
{assert(pst);pst->a = NULL;pst->capacity = 0;//在后面往栈里面放元素的时候,最后的top是在栈顶的下一个位置pst->top = 0;//如果是-1,那么代表的就是下标//pst->top = -1;
}
void STDestory(ST* pst)
{free(pst->a);pst->a = NULL;pst->capacity = pst->top = 0;
}
//入栈
void STPush(ST* pst, STDataType x)
{assert(pst);if (pst->top == pst->capacity)//如果top的数值刚好等于栈的总容量,那么栈就是满了,包含为0的情况。{int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);//用realloc改变我们申请动态存储空间的大小if (tmp == NULL){perror("STPush::relloc");return;}pst->a = tmp;//成功改变了空间,就赋值给栈里的a。pst->capacity = newcapacity;//总体的容量也随之变化。}pst->a[pst->top] = x;pst->top++;//因为我们的top的栈顶的位置,所以在每一次赋值完毕后就递增。
}//出栈
void STPop(ST* pst)
{assert(pst);assert(pst->top > 0);pst->top--;
}//读取栈顶元素
STDataType STTop(ST* pst)
{assert(pst);assert(pst->top > 0);return pst->a[pst->top-1];
}//判空
bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}
//获取数据个数
int STSize(ST* pst)
{assert(pst);return pst->top;
}
bool isValid(char* s) 
{ST p;STInit(&p);//初始化栈int sz=strlen(s);//求出栈里的总元素for(int i=0;i<sz;i++){if(s[i]=='('||s[i]=='['||s[i]=='{'){STPush(&p,s[i]);//如果与三种左括号匹配上了,就入栈}else//否则就是右括号的情况{if(STEmpty(&p))//这里有一个判空,目的就是我们的栈里必须要有左括号,不然我们已经进入else了,这里就只有可能是右括号,而这个右括号就不可能与左括号匹配上了{STDestory(&p);//如果进入了这个if语句,就说明不可能匹配上了return false;}char top=STTop(&p);//取出都是左括号栈里的头栈元素STPop(&p);if((top=='('&&s[i]!=')')||(top=='['&&s[i]!=']')||(top=='{'&&s[i]!='}')){STDestory(&p);//这个if语句就是,虽然栈里有左括号,同时也匹配到了右括号,但是括号直接不匹配的问题return false;}}}bool ret=STEmpty(&p);//如果栈为空了,就是说所以的括号都匹配上了STDestory(&p);return ret;
}

这个题主要需要注意的地方就是我们入栈入的都是左括号,右括号就单独的一个一个的与栈里的左括号相匹配。总的来说不算难,就是让我们了解一下栈的应用。

二.用队列实现栈

OJ链接

 简单的说呢,就是我们使用队列的先进先出的特点来完成栈的先进后出的特点。对于队列不熟悉的朋友,也可以看我们上一篇博客。

//先来完成队列的实现
typedef int QDataType;
typedef struct QNode//我们用链表来实现队列
{QDataType val;struct QNode* next;
}QNode;
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;
//队列初始化
void QInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;pq->size = 0;
}
//队列的销毁
void QDestroy(Queue* pq)
{assert(pq);QNode* pcur = pq->phead;while (pcur)//遍历整个链表{QNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = pq->ptail = NULL;pq->size = 0;
}
//入队
void QPush(Queue* pq, QDataType x)
{assert(pq);QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("newnode");return;}newnode->next = NULL;newnode->val = x;if (pq->size == 0)//链表没有节点{pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}pq->size++;//用size记录链表节点的个数,也就是队列里元素的个数
}
//出队列
void QPop(Queue* pq)
{assert(pq);assert(pq->phead);//出队列之前队列里一定要有元素if (pq->phead == pq->ptail)//一个节点{free(pq->phead);pq->phead = pq->ptail = NULL;}else//多个节点{QNode* pcur = pq->phead->next;free(pq->phead);pq->phead = pcur;}pq->size--;//出一个,减一个
}
//取队头元素
QDataType QTop(Queue* pq)
{assert(pq);assert(pq->phead);return pq->phead->val;
}
//取队尾元素
QDataType QBack(Queue* pq)
{assert(pq);assert(pq->ptail);return pq->ptail->val;
}
//求队列里元素个数
int QSize(Queue* pq)
{assert(pq);return pq->size;
}
//判空
bool QEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}//开始写题
typedef struct {Queue q1;Queue q2;
} MyStack;//创建两个队列//初始化栈
MyStack* myStackCreate()
{MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QInit(&(pst->q1));QInit(&(pst->q2));//因为pst->q是队列,所以调用上面写的初始化队列的函数return pst;
}
//入栈
void myStackPush(MyStack* obj, int x) {if (!QEmpty(&(obj->q1)))//这里的入队列,我们入的队列是为空的那一个{QPush(&(obj->q1), x);}else{QPush(&(obj->q2), x);}
}
//出栈
int myStackPop(MyStack* obj)
{//假设法,创建两个新的队列,一个为空队列,一个不为空Queue* empty = &(obj->q1);Queue* noempty = &(obj->q2);if (!QEmpty(&(obj->q1))){empty = &(obj->q2);noempty = &(obj->q1);}//假设法完成后我们就只需要对empty和noempty进行改变while (QSize(noempty) > 1)//这里的循环和大于1的意思是,我们要把非空的队列入到为空的队列里,但是要剩余一个元素。{QPush(empty, QTop(noempty));QPop(noempty);}int top = QTop(noempty);//上面我们剩余的那个元素就是作为栈先进后出的那个元素(后面再单独解释一下)QPop(noempty);return top;
}
//提取栈顶元素
int myStackTop(MyStack* obj) {if (!QEmpty(&(obj->q1))){return QBack(&(obj->q1));//队尾实际上就是栈顶}else{return QBack(&(obj->q2));}
}
//判空
bool myStackEmpty(MyStack* obj) {return QEmpty(&(obj->q1)) && QEmpty(&(obj->q2));
}void myStackFree(MyStack* obj) {QDestroy(&(obj->q1));QDestroy(&(obj->q2));free(obj);
}

不能理解的可以看一下图:

相对于队列来说,如果要实现先进后出的特点,就要不断的往空队列移动元素,且移动的时候一定要保留一个,这个保留的元素相对于栈来说,就是我们要出栈的第一个元素。 

三.用栈实现队列

OJ链接

同样的,解决这道题,依然要先实现一下栈。

typedef int SLTDataType;
typedef struct Stack
{SLTDataType* a;int top;int capacity;
}ST;
void STInit(ST* pst)
{assert(pst);pst->a = NULL;pst->capacity = 0;//top的位置在栈顶的后面的一个位置pst->top = 0;
}
void STDestory(ST* pst)
{assert(pst);free(pst->a);pst->a = NULL;pst->capacity = pst->top = 0;
}
void STPush(ST* pst, SLTDataType x)
{assert(pst);if (pst->top == pst->capacity){int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;SLTDataType* tmp = (SLTDataType*)realloc(pst->a, newcapacity * sizeof(SLTDataType));if (tmp == NULL){perror("STPush::realloc");return;}pst->a = tmp;pst->capacity = newcapacity;}pst->a[pst->top] = x;pst->top++;
}
void STPop(ST* pst)
{assert(pst);assert(pst->top > 0);pst->top--;
}
SLTDataType STTop(ST* pst)
{assert(pst);assert(pst->top > 0);return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{assert(pst);return pst->top == 0;
}//正式开始实现队列
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);//把需要入队的元素全部放在pushst里
}
//出队+删除
int myQueuePop(MyQueue* obj) {int front=myQueuePeek(obj);//调用下面的Peek函数,取出要出队的元素STPop(&obj->popst);然后删除return front;
}
//出队
int myQueuePeek(MyQueue* obj) {if(STEmpty(&obj->popst))//判断要popst是否为空{while(!STEmpty(&obj->pushst))//popst为空了并且pushst里面有元素{int top=STTop(&obj->pushst);//提取pushst里的元素STPush(&obj->popst,top);//放到popst里面STPop(&obj->pushst);//删除刚刚出去的pushst里的栈顶元素}}//这里的if语句和while语句的作用就是把pushst里的元素反过来放入到popst里面return STTop(&obj->popst);//最后在返回出栈的值,这个值就是我们要的先进先出的值
}
//判空
bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}
//释放
void myQueueFree(MyQueue* obj) {STDestory(&obj->pushst);STDestory(&obj->popst);free(obj);
}

这道题的思路就是先创建两个栈,一个专门用来入,一个专门用来出。要出栈的话就用popst。

等到我们把所有的元素都移动到popst里面的时候,再用栈的性质出栈,此时出来的元素的顺序将会是1,2,3,4。符合我们需要的特性,大家也可以试试入栈什么的。

四.设计循环队列

OJ链接

typedef struct {int* a;int head;int tail;//指向尾的下一个int k;//k是队列里所有的元素个数的总数
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->a = (int*)malloc(sizeof(int) * (k + 1));//多创建一个sizeof(int)obj->head = 0;obj->tail = 0;obj->k = k;return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->tail == obj->head;//如果head==tail就说明没有元素
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->tail + 1) % (obj->k + 1) == obj->head;//这里取了一个模,是为了避免tail在k位置时的情况
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {if (myCircularQueueIsFull(obj))//满了就不能入了return false;obj->a[obj->tail] = value;//tail指向的是队尾的下一个位置obj->tail++;//可以分为正常情况和tail在k位置上的情况obj->tail %= (obj->k + 1);//有这个主要是为了避免tail在K位置上的情况return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return false;obj->head++;//删除队头的元素,直接head++就行了obj->head %= (obj->k + 1);//跟tail在k位置是同一个道理return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;return obj->a[obj->head];//直接返回队头元素
}int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[(obj->tail + obj->k) % (obj->k + 1)];//因为tail指向的是队尾的下一个位置,所以要小心一下tail在k位置的情况
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->a);free(obj);
}

这里需要单独解释一下一个东西:

int myCircularQueueRear(MyCircularQueue* obj) {if (myCircularQueueIsEmpty(obj))return -1;elsereturn obj->a[(obj->tail + obj->k) % (obj->k + 1)];//因为tail指向的是队尾的下一个位置,所以要小心一下tail在k位置的情况
}

注意我return的是什么:(obj->tail + obj->k) % (obj->k + 1)这个东西又可以写成(obj->tail-1 + obj->k+1) % (obj->k + 1)obj->tail-1加上一个obj->k + 1再取obj->k + 1的余。最后的结果不会有改变,但是多加了这一步就完美的避免了,tail在0位置的情况。如果直接return的是tail-1会存在访问越界的问题。

到这里这四个OJ题就结束了,感谢大家的观看,如有错误,还请多多指出。 

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内网环境安装使用DBeaver使用第一天

之前一直使用navicat&#xff0c;现在出于某种原因不让使用了&#xff0c;于是上手了这个工具&#xff0c;说实话&#xff0c;真的&#xff0c;但是必须要用。 首先安装的时候&#xff0c;必须要选择MySQL驱动&#xff0c;如果外网直接选择以后就可以下载了&#xff0c;内网需…

字符串函数(一):strcpy(拷贝),strcat(追加),strcmp(比较),及strncpy,strncat,strncmp

字符串函数 一.strcpy&#xff08;字符串拷贝&#xff09;1.函数使用2.模拟实现 二.strcat&#xff08;字符串追加&#xff09;1.函数使用2.模拟实现 三.strcmp&#xff08;字符串比较&#xff09;1.函数使用2.模拟实现 四.strncpy1.函数使用2.模拟实现 五.strncat1.函数使用2.…

采油厂职工向媒体投稿的好方法找到了

作为一名采油厂的职工,我深知在媒体上定期投稿的重要性。这不仅是我们展示工作成果、传播企业文化的重要途径,更是上级考核我们工作表现的一项指标。然而,在投稿的过程中,我经历了不少心酸与困扰。 起初,我采用传统的邮箱投稿方式。每天,我都会花费大量时间在网络上搜索合适的媒…

Linux----正则表达式练习题题解

1、 显示/etc/rc.d/rc.sysinit文件中以不区分大小的h开头的行&#xff1b; [rootopenEuler ~]# grep -E "^(H|h)" /etc/passwd halt:x:7:0:halt:/sbin:/sbin/halt 注&#xff1a;当然也可以使用grep -i来实现&#xff0c;这里我换了一个文件&#xff08;/etc/passw…

基于SPWM控制策略的二极管钳位型的五电平逆变器simulink仿真

本人搭建了二极管钳位型五电平逆变器simulink仿真模型&#xff0c;SPWM采用层叠&#xff0c;输出线电压9电平&#xff0c;相电 压5电平&#xff0c;滤波后对称三相电压、电流&#xff0c;THD<5%&#xff0c;效果十分优越&#xff0c;适合新手学习使用。 模型获取链接&…

使用apache和htaccess对目录访问设置密码保护配置教程

对目录设置密码保护配置说明 我们有时候访问某些网站的时候&#xff0c;要求输入用户名和密码才能访问。这是为了保护隐私&#xff0c;只让经过许可的人访问。 在本教程中主要介绍两种方法&#xff0c;一种是通过apache httpd.conf配置文件对管理后台目录设置密码保护&#xff…

252 基于MATLAB的自适应差分阈值法检测心电信号的QRS波

基于MATLAB的自适应差分阈值法检测心电信号的QRS波&#xff0c;QRS波群反映左、右心室除极电位和时间的变化&#xff0c;第一个向下的波为Q波&#xff0c;向上的波为R波&#xff0c;接着向下的波是S波。通过GUI进行数据处理&#xff0c;展示心率和QRS。程序已调通&#xff0c;可…

Mysql数据类型设计思考

一、Mysql数据类型设计规范 1.1 选择更小的数据类型 一般情况下&#xff0c;在满足存储要求的基础上&#xff0c;尽量选择小的存储类型。例如&#xff1a;存储0~200&#xff0c;tinyint和bigint都可以存储&#xff0c;那么选择tinyint。原因&#xff1a;越小的数据类型运算速…

迄今为止最全- 前端性能优化

简介 当我们说前端性能优化的时候&#xff0c;指的可能是不同场景的性能优化。前端涉及性能优化的场景主要有&#xff1a; 项目构建性能优化 页面性能优化 加载时性能优化 运行时性能优化 构建性能主要指构建速度&#xff0c;优化方法和打包工具直接相关&#xff0c;主要…

高效管理文件技巧:一键利用文件大小,轻松移动到指定文件夹

在日常生活和工作中&#xff0c;我们经常需要管理大量的文件&#xff0c;包括文档、图片、视频等各种类型。然而&#xff0c;手动分类和移动这些文件往往是一项繁琐且耗时的任务。为了提高文件管理的效率&#xff0c;我们可以采用一些高效的管理技巧&#xff0c;比如利用文件大…

GBase 8s 数据库集群切换及恢复

GBase 8s 数据库切换分为自动切换、由CM控制的按FOC规则的切换、手工切换。 自动切换 全自动切换用于HAC集群中&#xff0c;由于集群只有两个节点&#xff0c;数据库相互之前进行状态检查&#xff0c;发现异常时&#xff0c;能按DRAUTO的配置方式进行自动切换。 在HAC集群中&…

3.整数运算

系列文章目录 信息的表示和处理 : Information Storage&#xff08;信息存储&#xff09;Integer Representation&#xff08;整数表示&#xff09;Integer Arithmetic&#xff08;整数运算&#xff09;Floating Point&#xff08;浮点数&#xff09; 文章目录 系列文章目录前…

基于Echarts的大数据可视化模板:服务器运营监控

目录 引言背景介绍研究现状与相关工作服务器运营监控技术综述服务器运营监控概述监控指标与数据采集可视化界面设计与实现数据存储与查询优化Echarts与大数据可视化Echarts库以及其在大数据可视化领域的应用优势开发过程和所选设计方案模板如何满足管理的特定需求模板功能与特性…

Android:资源的管理,Glide图片加载框架的使用

目录 一&#xff0c;Android资源分类 1.使用res目录下的资源 res目录下资源的使用&#xff1a; 2.使用assets目录下的资源 assets目录下的资源的使用&#xff1a; 二&#xff0c;glide图片加载框架 1.glide简介 2.下载和设置 3.基本用法 4.占位符&#xff08;Placehold…

[ 视频号]代替用户发布视频api

使用接口&#xff0c;替代用户使用设备直接发布视频api 接口地址&#xff1a; http://接口地址/api/v2 先调用登录接口&#xff0c;进行账号登录 登录二维码接口入参&#xff1a; {"appId": "","proxyIp": "","regionId"…

WWW服务器搭建(1)——HTTP协议原理篇

目录 一、WWW的相关概念 1.1 WWW的定义 1.2 超文本标记语言HTML 1.3 统一资源定位符URL 1.4 超文本传输协议HTTP 二、HTTP协议工作过程 2.1 DNS解析 2.2 TCP连接过程 2.3 HTTP 请求与响应 2.4 TCP连接断开 三、HTTP请求报文格式 3.1 请求行 3.2 请求头 3.3 空行 …

Docker部署Azure chatgpt样例应用

Github地址 https://github.com/microsoft/sample-app-aoai-chatGPT?tabreadme-ov-file#environment-variables 使用Amazon Linux 2 AMI 的arm 64版本镜像, t4g.medium实例。 需要安装git&#xff0c;可能需要安装 pip3等 sudo apt-get install -y python3-pip 然后从如下…

安卓短视频一键搬运软件_V1.5.2 高级版

短视频一键搬运app是一款非常实用的视频处理软件&#xff0c;拥有各种各样的视频处理功能&#xff0c;可以帮助用户进行视频的多项处理&#xff0c;首先用户可以在这里为视频去除水印&#xff0c;打开视频文件过后&#xff0c;再把视频里面的水印内容框选出来&#xff0c;这样就…

外卖 点金推广实战课程,2024外卖 点金推广全流程(7节课+资料)

课程内容&#xff1a; 外卖点金推广实操课程 资料 01 1-了解外卖.mp4 02 第一节:点金推广的说明.mp4 03 第二节:如何降低点金推广的成本,mp4 04 第三节:如何计算点金推广的流速,mp4 05 第四节:如何提升点金的精准度,mp4 06 第五节:点金推广实操,mp4 07 点金推广高级教程…

volatile详解、原理

文章目录 一、Volatile的定义和作用1.1 Volatile简介1.2 Volatile作用 二、并发编程中的三个问题&#xff1a;可见性、原子性、有序性二、Java内存模型&#xff08;JMM&#xff09;三、volatile变量的特性3.1 线程可见性3.2 禁止重排序禁止重排序原理禁止重排序举例 3.3 volati…