【数据结构】面试OJ题———栈|队列|互相实现|循环队列|括号匹配

目录

1. 有效的括号

思路:

2.用队列实现栈 

思路:

3.用栈实现队列

思路:

 4.设计循环队列

思路:


1. 有效的括号

20. 有效的括号 - 力扣(LeetCode)

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

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

思路:

左右括号匹配,有两个需要考虑的点;

1.括号顺序问题;

2.括号数量问题;

1.括号顺序问题:括号都有对应的左右边,出现这样的 ({)} 就是错误的;只有相应的左括号会有对应的右括号在旁边;

栈:先进后出;一组括号,先比较的是最后输入的左括号;所以栈满足此功能;

队列:先进先出;最先输入的左括号应该是与最后输入的右括号比较,所以队列不满足此功能;

2.括号顺序问题: 即使右括号都满足了左括号,会有残留问题,比如右括号多些,而此时空间已经为空;

我们构建一个栈的基本功能 ;【数据结构】——栈|队列(基本功能)-CSDN博客

// 支持动态增长的栈
typedef char STDataType;
typedef struct Stack
{STDataType* arr;	int top;		// 栈顶int capacity;  // 容量 
}Stack;// 初始化栈 
void StackInit(Stack* ps)
{assert(ps);assert(ps);ps->arr = NULL;ps->capacity = 0;ps->top = -1;	//表示栈顶元素
}// 入栈 
void StackPush(Stack* ps, STDataType data)
{//检查容量if (ps->top + 1 == ps->capacity)	//top表示的是栈顶元素,先++top,再插入的,所以检查+1位置是否可用{int newcapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* newnode = (STDataType*)realloc(ps->arr,sizeof(STDataType) * newcapacity);assert(newnode);	//七匹狼式检查是否开辟成功ps->arr = newnode;ps->capacity = newcapacity;}ps->top++;ps->arr[ps->top] = data;
}// 出栈 
void StackPop(Stack* ps)
{assert(ps);assert(ps->top >= 0);ps->top--;
}// 获取栈顶元素 
STDataType StackTop(Stack* ps)
{assert(ps);assert(ps->top >= 0);return ps->arr[ps->top];
}// 检测栈是否为空,如果为空返回非零结果,如果不为空返回0 
bool StackEmpty(Stack* ps)
{assert(ps);if (ps->top < 0)	//为空{return true;}else{return false;}
}// 销毁栈 
void StackDestroy(Stack* ps)
{assert(ps);ps->capacity = 0;ps->top = -1;free(ps->arr);ps->arr = NULL;
}

当括号字符串 所有左括号都存储进栈中,当遇到右括号就开始逐个和栈顶括号比较;

 这里在于比较时,因为比较后还需要继续比较,所以采取找到不符合的条件跳出循环;

在字符串循环结束后,要检查是否栈为空

bool isValid(char* s) {Stack ps;StackInit(&ps);while(*s){//左括号入栈if(*s == '(' || *s == '{' || *s == '['){StackPush(&ps,*s);}//右括号与栈顶比较else{//检查是否数量匹配,检查栈是否还有元素,为空返回非0if(StackEmpty(&ps)){StackDestroy(&ps);return false;}//左右括号比较,不相等返回faslechar tmp = StackTop(&ps);StackPop(&ps); //移除栈顶元素if((tmp != '(' && *s == ')')||( tmp != '{' && *s == '}')|| (tmp != '[' && *s == ']')){StackDestroy(&ps);return false;}}++s;}bool tmp = StackEmpty(&ps);StackDestroy(&ps);return tmp;
}

2.用队列实现栈 

225. 用队列实现栈 - 力扣(LeetCode)

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

实现 MyStack 类:

  • void push(int x) 将元素 x 压入栈顶。
  • int pop() 移除并返回栈顶元素。
  • int top() 返回栈顶元素。
  • boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

思路:

 栈是先进后出的结构,而队列是先进先出的;

两个队列,数据存储到队列1中后,按照先进先出的结构,将size(元数个数)-1移动到队列2中,再输出队列1的话,就实现了栈的结构,先进后出;

可以总结为:如果要输入元素,就输入到空队列中

//将元素X压入栈顶
void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1))   //为空返回非0,取反{QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}
}

如何找到栈顶元素呢,我们可以将不为空的队列中 size(有效元素个数)-1个 数据移动到空队列中,再输出原不为空的队列;

我们采取假设法,找到不为空的队列,假设某一队列为empty,另外一个队列为noempty;然后if条件判断假设; 

//移除并返回栈顶元素
int myStackPop(MyStack* obj) {//将size-1个元素移动到 另一个空队列中//假设q1队列为空,q2不为空Queue* empty = &obj->q1;Queue* noempty = &obj->q2;//验证假设if(!QueueEmpty(&obj->q1)){empty = &obj->q2;noempty = &obj->q1;}//将size-1个元素 移动到空队列中while(QueueSize(noempty)>1){QueuePush(empty,QueueFront(noempty));QueuePop(noempty);}int top = QueueFront(noempty);    //此刻该队列仅有需要的数据QueuePop(noempty);return top;}

 这两个是这道题较为麻烦的函数,另外的函数需求因为较为简单,我就直接码上讲解;

且这段代码是这道题一半的代码,另外一般就是队列的基本功能实现代码,可以去【数据结构】——栈|队列(基本功能)-CSDN博客获取完整的代码,复制过来即可;

注:这里释放内存是由里往外,因为结构定义了多重 

 

MyStack* myStackCreate() {MyStack* pst =  (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}//将元素X压入栈顶
void myStackPush(MyStack* obj, int x) {if(!QueueEmpty(&obj->q1))   //为空返回非0,取反{QueuePush(&obj->q1,x);}else{QueuePush(&obj->q2,x);}
}
//移除并返回栈顶元素
int myStackPop(MyStack* obj) {//将size-1个元素移动到 另一个空队列中//假设q1队列为空,q2不为空Queue* empty = &obj->q1;Queue* noempty = &obj->q2;//验证假设if(!QueueEmpty(&obj->q1)){empty = &obj->q2;noempty = &obj->q1;}while(QueueSize(noempty)>1){QueuePush(empty,QueueFront(noempty));QueuePop(noempty);}int top = QueueFront(noempty);QueuePop(noempty);return top;}
//返回栈顶元素
int myStackTop(MyStack* obj) {if(!QueueEmpty(&obj->q1))   //为空返回非0,取反{return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);}
}//如果栈是空,返回true,非空 返回fasle
bool myStackEmpty(MyStack* obj) {if(QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2))    //表示为空{return true;}elsereturn false;
}void myStackFree(MyStack* obj) {    //注意释放顺序QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);free(obj);
}

3.用栈实现队列

232. 用栈实现队列 - 力扣(LeetCode)

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

实现 MyQueue 类:

  • void push(int x) 将元素 x 推到队列的末尾
  • int pop() 从队列的开头移除并返回元素
  • int peek() 返回队列开头的元素
  • boolean empty() 如果队列为空,返回 true ;否则,返回 false

思路:

 这道题和题2相似,要实现1先出去,就是先把入栈的元素全部移动到出栈中,就实现了队列的结构;

与之不同的是:这边需要把全部的数据移动到另一个栈;

可以定义两个栈为:

pushst栈:用来数据存放的栈;

popst栈: 用来输出数据的栈;

1.需要获得栈内数据时,先检查popst栈是否为空,如果不为空,返回该栈数据即刻;

2.如果为空,先将pushst栈数据导入到popst栈内,在进行返回popst栈数据 

int myQueuePeek(MyQueue* obj) {if(!STEmpty(&obj->popst))   //popst栈有数据  直接返回栈顶元素{return STTop(&obj->popst);}else{//先将数据导入到popst栈while(!STEmpty(&obj->pushst)){STPush(&obj->popst,STTop(&obj->pushst));STPop(&obj->pushst);}//返回栈顶元素return STTop(&obj->popst);}
}

 注:这里和题2一样,注意释放顺序

因为其他功能较为思路明了,解析过程会在代码中解释; 

栈函数接口实现功能代码在【数据结构】——栈|队列(基本功能)-CSDN博客

MyQueue* myQueueCreate() {MyQueue* obj = (MyQueue*)malloc(sizeof(MyQueue));STInit(&obj->pushst);STInit(&obj->popst);return obj;
}void myQueuePush(MyQueue* obj, int x) {//数据进到pushst栈,STPush(&obj->pushst,x);
}int myQueuePop(MyQueue* obj) {int front = myQueuePeek(obj);STPop(&obj->popst);return front;
}int myQueuePeek(MyQueue* obj) {if(!STEmpty(&obj->popst))   //popst栈有数据  直接返回栈顶元素{return STTop(&obj->popst);}else{//先将数据导入到popst栈while(!STEmpty(&obj->pushst)){STPush(&obj->popst,STTop(&obj->pushst));STPop(&obj->pushst);}//返回栈顶元素return STTop(&obj->popst);}
}bool myQueueEmpty(MyQueue* obj) {if(STEmpty(&obj->pushst) && STEmpty(&obj->popst)){return true;}elsereturn false;
}void myQueueFree(MyQueue* obj) {STDestrory(&obj->pushst);STDestrory(&obj->popst);free(obj);
}

 4.设计循环队列

622. 设计循环队列 - 力扣(LeetCode)

 设计你的循环队列实现。 循环队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

思路:

 这里有两种数据结构可以选择:数组和链表;

链表对后续的操作比较方便,但是不好控制,而且构建的时候也极为麻烦;

如果用数组 需要考虑的是如何规避为满和为空的判断

 

采用数组的话,我们定义front,back;来进行判断循环点;

 

这里的问题就是如何规避 为满和为空的问题; 

解决:1.可以定义一个size;在front == back的基础上,size == 0 就是空

                                                                                      size != 0就是满;

        2. 可以多开辟一块空间;动态空间;

 

多开辟一块空间,这块空间是动态移动的,但是不存储数据;

当front == back时,就是空;

当 back+1 == front 就是满,

这里因为数组 如果在边界点的话 会导致越界,

所以采取取模来规范范围;也达到了循环的条件

 这里需要注意的就是获取最后一个结点;

因为back是指向最后一个数据的下一个位置;如果back在边界 -1 有可能越界;

需要特殊处理规范这块;

 

取模的操作:如果该数大于本身,就不会有影响; 

这里删除和增加一个数据,都有可能引起越界;所以在对back front操作后,都要规范范围;

取模实际空间大小 

typedef struct {int* parr;  //动态开辟int front;  //头结点int back;   //指向尾结点的下一个int size;      //实际开辟的空间
} MyCircularQueue;MyCircularQueue* myCircularQueueCreate(int k) {MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));obj->parr = (int*)malloc(sizeof(int)*(k+1));   //多开辟一块空间obj->front = 0;obj->back = 0;obj->size= k+1;return obj;
}bool myCircularQueueIsEmpty(MyCircularQueue* obj) {return obj->front == obj->back;
}bool myCircularQueueIsFull(MyCircularQueue* obj) {return (obj->back+1) % obj->size == obj->front;  //满了返回真
}bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {//先判断是否满了if(myCircularQueueIsFull(obj))  //满了返回真return false;obj->parr[obj->back] = value;obj->back++;obj->back %= obj->size;return true;
}bool myCircularQueueDeQueue(MyCircularQueue* obj) {//判断是否为空if(myCircularQueueIsEmpty(obj))return false;obj->front++;obj->front %= obj->size;return true;
}int myCircularQueueFront(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->parr[obj->front];
}int myCircularQueueRear(MyCircularQueue* obj) {if(myCircularQueueIsEmpty(obj))return -1;return obj->parr[(obj->back-1 + obj->size) % obj->size];
}void myCircularQueueFree(MyCircularQueue* obj) {free(obj->parr);free(obj);
}

 

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

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

相关文章

Hive SQL间隔连续问题

问题引入 下面是某游戏公司记录的用户每日登录数据, 计算每个用户最大的连续登录天数&#xff0c;定义连续登录时可以间隔一天。举例&#xff1a;如果一个用户在 1,3,5,6,9 登录了游戏&#xff0c;则视为连续 6 天登录。 id dt1001 2021-12-121002 2021-12-12…

visual studio code 好用的插件

vscode-icons Better comments 该插件对不同类型的注释会附加了不同的颜色&#xff0c;更加方便区分&#xff0c;帮助我们在代码中创建更人性化的注释。 Error Lens Error Lens插件是一款可以检测你编写的代码的语法错误&#xff0c;并且会显示出对语法错误的诊断信息…

RCNN 学习

RCNN算法流程 RCNN算法流程可分为4个步骤 一张图像生成1K~2K个候选区域&#xff08;使用Selective Search方法&#xff09;对每个候选区域&#xff0c;使用深度网络图特征特征送入每一类的SVM分类器&#xff0c;判别是否属于该类使用回归期器细修正候选框位置 1.候选区域的生…

ChatGPT是科学还是艺术?

OpenAI最近谈到GPT4变懒的问题&#xff0c;说“它更像是多人共同参与的艺术创作”&#xff0c;那到底大模型是科学还是艺术&#xff1f;

公式识别任务各个链条全部打通

目录 引言公式识别任务是什么&#xff1f;公式识别任务解决方案初探使用建议写在最后 引言 随着LaTeX-OCR模型转换问题的解决&#xff0c;公式识别任务中各个链条已经全部打通。小伙伴们可以放开膀子干了。 解决业界问题的方案&#xff0c;并不是单独训练一个模型就完事了&am…

如何确认网站是否有漏洞,如何找出网站存在的漏洞,找到漏洞该如何处理

如何确认网站或者服务器是否有漏洞 判断一个网站是否是存在漏洞的方法&#xff1a; 1.可以借助德迅云安全漏洞扫描功能来检查漏洞。 2.打开德迅云安全首页&#xff0c;点击最上面导航栏中的“安全产品”。 3.滑到“漏洞扫描”&#xff0c;选择“产品价格”服务。 4.选择您需…

【力扣】141和142环形链表

141.环形链表 法一&#xff1a;快慢指针 思路&#xff1a; 用两个指针slow,fast,后者能比前者多走一步路&#xff0c;那判断是不是有环&#xff0c;只需要判断是否会相遇。 就是有一个能比乌龟跑2倍快的兔子&#xff0c;两小只都在有环的路上跑&#xff0c;那是不是肯定会相…

golang开发之个微机器人的二次开发

简要描述&#xff1a; 下载消息中的文件 请求URL&#xff1a; http://域名地址/getMsgFile 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 参数&#xff1a; 参数名必选类型…

java基础之TreeMap详解

TreeMap详解 TreeMap是Map接口的一个实现类&#xff0c;底层基于红黑树的实现&#xff0c;按照key的顺序存储 TreeMap 从继承结构可以看到TreeMap除了继承了AbstractMap类&#xff0c;还实现了NavigableMap接口&#xff0c;而NavigableMap接口是继承自SortedMap接口的&#xff…

使用Vue3+Typescript手写一个日历签到组件

设计理念 昨天写了个简单美观的日历签到组件&#xff0c;使用的是Vue3TypeScript&#xff0c;大概逻辑是先找到本月份第一天是周几&#xff0c;然后开始填充月份日期&#xff1a;weeksArray:[[]]:之后渲染到表格中&#xff0c;对于签到事件触发则先判断是否是今天且还未没有签…

西南科技大学数字电子技术实验三(MSI逻辑器件设计组合逻辑电路及FPGA的实现)FPGA部分

一、实验目的 进一步掌握MIS(中规模集成电路)设计方法。通过用MIS译码器、数据选择器实现电路功能,熟悉它们的应用。进一步学习如何记录实验中遇到的问题及解决方法。二、实验原理 1、4位奇偶校验器 Y=S7i=0DiMi D0=D3=D5=D6=D D1=D2=D4=D7= `D 2、组合逻辑电路 F=A`B C …

面试计算机网络八股文五问五答第二期

面试计算机网络八股文五问五答第二期 作者&#xff1a;程序员小白条&#xff0c;个人博客 相信看了本文后&#xff0c;对你的面试是有一定帮助的&#xff01; ⭐点赞⭐收藏⭐不迷路&#xff01;⭐ 1.OSI七层协议&#xff1f; 2. TCP和UDP传输协议的区别&#xff1f; TCP是可…

为什么要用向量检索

之前写过一篇文章&#xff0c;是我个人到目前阶段的认知&#xff0c;所做的判断。我个人是做万亿级数据的搜索优化工作的。一直在关注任何和搜索相关的内容。 下一代搜索引擎会什么&#xff1f;-CSDN博客 这篇文章再来讲讲为什么要使用向量搜索。 在阅读这篇文章之前呢&#xf…

【网络安全】网络设备可能面临哪些攻击?

网络设备通常是网络基础设施的核心&#xff0c;并控制着整个网络的通信和安全&#xff0c;同样面临着各种各样的攻击威胁。 对网络设备的攻击一旦成功&#xff0c;并进行暴力破坏&#xff0c;将会导致网络服务不可用&#xff0c;且可以对网络流量进行控制&#xff0c;利用被攻陷…

【JavaEE】线程池

作者主页&#xff1a;paper jie_博客 本文作者&#xff1a;大家好&#xff0c;我是paper jie&#xff0c;感谢你阅读本文&#xff0c;欢迎一建三连哦。 本文于《JavaEE》专栏&#xff0c;本专栏是针对于大学生&#xff0c;编程小白精心打造的。笔者用重金(时间和精力)打造&…

springcloud分布式事务

文章目录 一.为什么引入分布式事务?二.理论基础1.CAP定理2.BASE理论 三.Seata1.微服务集成Seata2.XA模式(掌握)3.AT模式(重点)4.TCC模式(重点)5.Saga模式(了解) 四.四种模式对比五.Seata高可用 一.为什么引入分布式事务? 事务的ACID原则 在大型的微服务项目中,每一个微服务都…

案例课4——智齿客服

1.公司介绍 智齿科技&#xff0c;一体化客户联络中心解决方案提供商。提供基于「客户联络中心」场景的一体化解决方案&#xff0c;包括公域私域、营销服务、软件BPO的三维一体化。 智齿科技不断整合前沿的人工智能及大数据技术&#xff0c;已构建形成呼叫中心、机器人「在线语音…

主机访问Android模拟器网络服务方法

0x00 背景 因为公司的一个手机app的开发需求&#xff0c;要尝试链接手机开启的web服务。于是在Android Studio的Android模拟器上尝试连接&#xff0c;发现谷歌给模拟器做了网络限制&#xff0c;不能直接连接。当然这个限制似乎从很久以前就存在了。一直没有注意到。 0x01 And…

分销电商结算设计

概述 分销电商中涉及支付与结算&#xff1b;支付职责是收钱&#xff0c;结算则是出钱给各利益方&#xff1b; 结算核心围绕业务模式涉及哪些费用&#xff0c;以及这些费用什么时候通过什么出资渠道&#xff0c;由谁给到收方利益方&#xff1b; 结算要素组成费用项结算周期出…

区块链的可拓展性研究【03】扩容整理

为什么扩容&#xff1a;在layer1上&#xff0c;交易速度慢&#xff0c;燃料价格高 扩容的目的&#xff1a;在保证去中心化和安全性的前提下&#xff0c;提升交易速度&#xff0c;更快确定交易&#xff0c;提升交易吞吐量&#xff08;提升每秒交易量&#xff09; 目前方案有&…