栈的2道面试题【有效的括号】【用栈实现队列】

栈的面试题:

1.有效的括号

题目:

有效的括号

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

有效字符串需满足:

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

示例 1:

输入:s = "()"
输出:true

示例 2:

输入:s = "()[]{}"
输出:true

示例 3:

输入:s = "(]"
输出:false

提示:

  • 1 <= s.length <= 104
  • s 仅由括号 '()[]{}' 组成

思路:

  1. 首先我们可以知道我们需要去比较字符串内的元素,并且我们需要用到后进先出的场景,因此这里我们考虑用栈来解决问题
  2. 我们将前括号放到栈内,s指针如果指向的是后括号,就让其和栈内的栈顶元素对比,如果匹配就将栈顶元素弹出,s继续遍历
  3. 一旦不匹配,或者栈空了,s还有后括号没有匹配,或者栈还有元素,s没有后括号匹配了就是无效字符串,返回false

代码实现:

由于我们是使用C语言写oj题,因此我们需要自己去编写栈的定义和栈的接口实现

如果是在leetcode上,头文件之类的自己会包含,我们不用去管

接口:
// 这里的栈我们用动态顺序表实现 (也可以用静态顺序表实现[不好扩容和定义空间大小])
# include<stdio.h>
# include<assert.h>
# include<stdlib.h>
# include<stdbool.h>typedef char SLDataType;
typedef struct Stack
{SLDataType* _a;int _top; // 栈顶下标 [规定栈顶下标:最后一个有效数据的下一个位置]int _capacity; // 数组的有效空间大小 
}Stack;// 栈的初始化
void StackInit(Stack* ps);// 栈的销毁
void StackDestory(Stack* ps);// 栈是能从栈顶  存数据或者取数据,因此不存在尾插头插之类的
// 入栈
void StackPush(Stack* ps, SLDataType x);// 出栈
void StackPop(Stack* ps);// 栈的数据个数获取
//int StackSize(Stack st); //其实理论上获取元素个数只需要传值调用就行 但是为了保持接口一致性,我们采用指针
int StackSize(Stack* ps);// 获取栈顶元素
SLDataType StackTop(Stack* ps);// 判断栈是否为空
int StackEmpty(Stack* ps); // 是空返回1  不是空的返回0// 栈的初始化
void StackInit(Stack* ps)
{assert(ps); // ps不能为NULL// 栈的初始化/*ps->_a = NULL;ps->_top = 0;ps->_capacity = 0;*/// 除了上面这种初始化。也可以这样初始化SLDataType* tmp = (SLDataType*)malloc(sizeof(SLDataType) * 4); // 这样后面入栈时无需判断 空间是否为0 if (tmp == NULL){perror("StackInit():malloc()");return;}ps->_a = tmp;ps->_top = 0;ps->_capacity = 4;
}// 栈的销毁
void StackDestory(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_top = ps->_capacity = 0;
}// 入栈
void StackPush(Stack* ps, SLDataType x)
{assert(ps);// 插入之前 判断栈的空间是否足够新的数据插入if (ps->_top == ps->_capacity) // 判断空间是否足够 {int newcapacity = ps->_capacity * 2;SLDataType* tmp = (SLDataType*)realloc(ps->_a, sizeof(SLDataType) * newcapacity); // 增容if (tmp == NULL) // 判断是否增容成功{perror("StackPush():realloc()");return;}// 更新栈ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = x; // 入栈ps->_top++; // 让top记录的是栈顶 也就是最后一个数据的下一个位置
}// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->_top > 0); // 栈里面要有数据才能出栈ps->_top--; // 让top--就行 最后一个数据的下标是 top - 1
}// 栈的数据个数获取
int StackSize(Stack* ps)
{assert(ps);return ps->_top; // top代表栈顶下标,是最后的一个数据的下标 + 1  其实就是栈的数据个数
}// 获取栈顶元素
SLDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_top > 0); // 没有数据还怎么获取return ps->_a[ps->_top - 1]; // top是栈顶下标,top - 1才是最后一个数据的下标
}// 判断栈是否为空
int StackEmpty(Stack* ps) // 是空返回1  不是空的返回0
{assert(ps);return ps->_top == 0 ? 1 : 0; // ps->pos只要为0就说明栈内没有数据了//return !ps->_top; // ps->top 为0 就返回1,为真就返回 0 ,除了0的数都是真
}
代码:
bool isValid(char* s)
{// 由于这道题需要用到后进先出的特性,因此我们使用栈来解决// 创建一个栈Stack st;StackInit(&st); // 初始化bool ret = true; // 用来判断字符串是否有效// 遍历字符串while (*s != '\0'){// 如果s指针指向的是前括号就入栈if (*s == '(' || *s == '[' || *s == '{'){StackPush(&st, *s);s++; // 让s往后走}else{// 走到这里有可能是s后括号多,栈内已经没有前括号了,那后面去取栈顶元素自然无法取出if (StackEmpty(&st))// 判断栈是否空了{// 走进来就说明栈内没有元素了,但是s还有后括号ret = false; // 无效字符串break;}// 判断s下一步指向的是否是后括号,是否匹配栈顶的前括号char top = StackTop(&st); // 取出栈顶元素// 每一种括号都要判断一下是否匹配到if (*s == ')' && top != '('){// 走到这里说明没有匹配上ret = false; // 无效字符串break; // 不在这里return false是因为会有内存泄漏问题,跳出循环去外面统一调用销毁函数}if (*s == ']' && top != '['){// 走到这里说明没有匹配上ret = false; // 无效字符串break;}if (*s == '}' && top != '{'){// 走到这里说明没有匹配上ret = false; // 无效字符串break;}// 走到这里说明有括号配对成功,让s继续往后遍历s++;// 栈顶元素匹配成功之后要弹出来,防止后面还有括号要配对StackPop(&st);}}// 走到这里,有可能是全部匹配完是true。 //也有可能是s字符串只有前括号比后括号多 退出循环时,栈内还有许多前括号if (!StackEmpty(&st)) // 判断栈是否为空ret = false; // 不是空的就是无效字符串StackDestory(&st); // 销毁栈if (ret == false)return false;// 走到这里就说明是有效字符串return true;
}

2.用栈实现队列

题目:

用栈实现队列

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

实现 MyQueue 类:

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

思路:

  1. 要通过两个栈实现先进先出的队列,我们要思考数据转移的特性

  2. 我们发现,我们把栈的数据转移到另外一个栈的时候,数据的顺序会倒转

  3. image-20240507223619789

  4. image-20240507223635988

  5. 然后我们发现,这样就是先进先出了,1,2, 3, 4压进去,出来也是从栈顶出来,1, 2, 3, 4。 也就是说 第一个栈的栈顶就是队列的队尾,第二个栈的栈顶就是队列的队头。

    • 那我们给这个队列插入数据时候,要从队尾插入,也就是要把数据从第二个栈全部转移到第一个栈。
    • 队列导出数据的时候,也就是从队头出,那就要把数据从第一个栈全部转移到第二个栈。

代码实现:

接口:
// 这里的栈我们用动态顺序表实现 (也可以用静态顺序表实现[不好扩容和定义空间大小])typedef int SLDataType;
typedef struct Stack
{SLDataType* _a;int _top; // 栈顶下标 [规定栈顶下标:最后一个有效数据的下一个位置]int _capacity; // 数组的有效空间大小 
}Stack;// 栈的初始化
void StackInit(Stack* ps);// 栈的销毁
void StackDestory(Stack* ps);// 栈是能从栈顶  存数据或者取数据,因此不存在尾插头插之类的
// 入栈
void StackPush(Stack* ps, SLDataType x);// 出栈
void StackPop(Stack* ps);// 栈的数据个数获取
//int StackSize(Stack st); //其实理论上获取元素个数只需要传值调用就行 但是为了保持接口一致性,我们采用指针
int StackSize(Stack* ps);// 获取栈顶元素
SLDataType StackTop(Stack* ps);// 判断栈是否为空
int StackEmpty(Stack* ps); // 是空返回1  不是空的返回0// 栈的初始化
void StackInit(Stack* ps)
{assert(ps); // ps不能为NULL// 栈的初始化/*ps->_a = NULL;ps->_top = 0;ps->_capacity = 0;*/// 除了上面这种初始化。也可以这样初始化SLDataType* tmp = (SLDataType*)malloc(sizeof(SLDataType) * 4); // 这样后面入栈时无需判断 空间是否为0 if (tmp == NULL){perror("StackInit():malloc()");return;}ps->_a = tmp;ps->_top = 0;ps->_capacity = 4;
}// 栈的销毁
void StackDestory(Stack* ps)
{assert(ps);free(ps->_a);ps->_a = NULL;ps->_top = ps->_capacity = 0;
}// 入栈
void StackPush(Stack* ps, SLDataType x)
{assert(ps);// 插入之前 判断栈的空间是否足够新的数据插入if (ps->_top == ps->_capacity) // 判断空间是否足够 {int newcapacity = ps->_capacity * 2;SLDataType* tmp = (SLDataType*)realloc(ps->_a, sizeof(SLDataType) * newcapacity); // 增容if (tmp == NULL) // 判断是否增容成功{perror("StackPush():realloc()");return;}// 更新栈ps->_a = tmp;ps->_capacity = newcapacity;}ps->_a[ps->_top] = x; // 入栈ps->_top++; // 让top记录的是栈顶 也就是最后一个数据的下一个位置
}// 出栈
void StackPop(Stack* ps)
{assert(ps);assert(ps->_top > 0); // 栈里面要有数据才能出栈ps->_top--; // 让top--就行 最后一个数据的下标是 top - 1
}// 栈的数据个数获取
int StackSize(Stack* ps)
{assert(ps);return ps->_top; // top代表栈顶下标,是最后的一个数据的下标 + 1  其实就是栈的数据个数
}// 获取栈顶元素
SLDataType StackTop(Stack* ps)
{assert(ps);assert(ps->_top > 0); // 没有数据还怎么获取return ps->_a[ps->_top - 1]; // top是栈顶下标,top - 1才是最后一个数据的下标
}// 判断栈是否为空
int StackEmpty(Stack* ps) // 是空返回1  不是空的返回0
{assert(ps);return ps->_top == 0 ? 1 : 0; // ps->pos只要为0就说明栈内没有数据了//return !ps->_top; // ps->top 为0 就返回1,为真就返回 0 ,除了0的数都是真
}
代码(自己实现的版本):
typedef struct 
{Stack _s1;Stack _s2;    
} MyQueue;MyQueue* myQueueCreate() 
{// 创建栈MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));StackInit(&pq->_s1);StackInit(&pq->_s2);return pq;
}void myQueuePush(MyQueue* obj, int x) 
{// 给队列插入元素,要在第一个栈插入// 如果第二个栈有数据,要将其全部转移到第一个栈if(!StackEmpty(&obj->_s2)){// 第二个栈的数据有数据,将其全部转移到第一个栈while(StackSize(&obj->_s2) > 0){// 转移StackPush(&obj->_s1, StackTop(&obj->_s2));// 让第二个栈的数据出栈StackPop(&obj->_s2);}}// 走到这里,如果第二个栈有数据,也全部转移到第一个栈// 如果第二个栈没有数据,那就直接在第一个栈插入数据就好StackPush(&obj->_s1, x);    
}int myQueuePop(MyQueue* obj) 
{// 要找到队头(队列开头的元素)就要把全部数据都放在第二个栈,栈顶的数据就是队头if(!StackEmpty(&obj->_s1)) {// 第一个栈的数据有数据,将其全部转移到第二个栈while(StackSize(&obj->_s1) > 0){// 转移StackPush(&obj->_s2, StackTop(&obj->_s1));// 让第一个栈的数据出栈StackPop(&obj->_s1);}}// 由于题目说了一个空的队列不会调用 pop 或者 peek 操作// 因此这里不用判断两个栈是否为空// 走到这里数据一定在第二个栈int ret = StackTop(&obj->_s2);StackPop(&obj->_s2); // 移除元素return ret;
}int myQueuePeek(MyQueue* obj) 
{// 要找到队头(队列开头的元素)就要把全部数据都放在第二个栈,栈顶的数据就是队头if(!StackEmpty(&obj->_s1)){// 第一个栈的数据有数据,将其全部转移到第二个栈while(StackSize(&obj->_s1) > 0){// 转移StackPush(&obj->_s2, StackTop(&obj->_s1));// 让第一个栈的数据出栈StackPop(&obj->_s1);}}// 由于题目说了一个空的队列不会调用 pop 或者 peek 操作// 因此这里不用判断两个栈是否为空// 返回队头,也就是第二个栈的栈顶数据return StackTop(&obj->_s2);
}bool myQueueEmpty(MyQueue* obj) 
{// 如果两个栈都为空,队列才是空return StackEmpty(&obj->_s1) && StackEmpty(&obj->_s2);
}void myQueueFree(MyQueue* obj) 
{StackDestory(&obj->_s1);StackDestory(&obj->_s2);free(obj);obj = NULL;
}
优化后的代码:
typedef struct 
{Stack _pushST; // 用于插入数据Stack _popST; // 用于出数据
} MyQueue;MyQueue* myQueueCreate() 
{// 创建栈MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));StackInit(&pq->_pushST);StackInit(&pq->_popST);return pq;
}void myQueuePush(MyQueue* obj, int x) 
{// 直接把数据插入到pushST栈内StackPush(&obj->_pushST, x);
}int myQueuePop(MyQueue* obj) 
{// 这个函数的功能和peek函数的功能就多了一个要移除,也就是让队头数据弹出// 那我们就考虑让代码复用int ret = myQueuePeek(obj);StackPop(&obj->_popST); // 代码复用return ret;
}int myQueuePeek(MyQueue* obj) 
{// 要找到队头 也就是popST的栈顶数据// 要分两种情况,//1.如果popST栈没有数据,那就把pushST栈的数据转移到popST栈内//2.如果popST有数据,直接返回栈顶的数据,这个数据就是队头if(!StackEmpty(&obj->_popST)){// popST有数据,直接返回栈顶数据,就是队头return StackTop(&obj->_popST);}else{// popST为空,将pushST栈的数据转移到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) 
{StackDestory(&obj->_popST);StackDestory(&obj->_pushST);free(obj);
}

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

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

相关文章

560. 和为 K 的子数组 974. 和可被 K 整除的子数组 【前缀和】

题目链接 ​​​​​​​974. 和可被 K 整除的子数组 560. 和为 K 的子数组 今天刷题的时候&#xff0c;刷了这两题&#xff0c;感觉挺有意思的。代码写起来挺简单的&#xff0c;但是思路和其中的细节以及涉及到的知识点确实让我挺意外的。这里写个博客解析一波&#xff0c;也是…

Python自动化下载指定公开页面文件

示例代码如下&#xff0c;但你拿到本地之需要做两件事才能运行 from selenium import webdriver from selenium.webdriver.chrome.service import Service from selenium.webdriver.common.by import By from selenium.webdriver.common.keys import Keys import time# 设置Se…

计算机组成原理(超详解!!) 第七节 中央处理器(上)

1.CPU的功能和组成 CPU的功能&#xff1a; 指令控制&#xff1a;程序的顺序控制。 操作控制&#xff1a;管理并产生每条指令的操作信号&#xff0c;并把它们送到相应部件&#xff0c;控制这些部件按要求进行动作。 时间控制&#xff1a;对各种操作实施…

Day_3

1. HttpClient HttpClient是Apache的一个子项目&#xff0c;是高效的、功能丰富的支持HTTP协议的客户端编程工具包 作用&#xff1a;发送HTTP请求&#xff0c; 接受相应数据 <dependency><groupId>org.apache.httpcomponents</groupId><artifactId>…

controlnet的preprocessor类型

GitHub - huggingface/controlnet_auxContribute to huggingface/controlnet_aux development by creating an account on GitHub.https://github.com/huggingface/controlnet_aux/https://huggingface.co/lllyasviel/sd_control_collection

智慧监控 高效运维

随着企业IT建设的不断深入和完善&#xff0c;IT管理的重要性逐渐被重视&#xff0c;打通数据割裂&#xff0c;使业务更加充分融合。亟需一套统一的平台来实现跨品牌跨设备类型的集中监控和管理。 LinkSLA带外监控平台&#xff0c;不仅适用于大规模或超大规模的运维场景&#x…

Spring MVC(一)

1 Spring MVC概述 我们在之前学习Servlet的时候&#xff0c;认识了在WEB开发中MVC设计模式&#xff0c;其最为经典的设计就是&#xff0c;通过控制器&#xff08;Controller&#xff09;分离模型&#xff08;Model&#xff09;和视图&#xff08;View&#xff09;。在具体的WEB…

Redis进阶学习

Redis进阶学习 一、Redis事务1.2 Redis监控1.3 Jedis连接1.4 SpringBoot整合1.5 自定义RedisTemple1.6 Redis.conf详解 二、 Redis持久化2.1 RDB2.2 AOF进程 三、Redis发布订阅3.1 Redis主从复制3.2 集群环境配置3.3、复制原理3.4、宕机后主动变为主机3.5、哨兵模式 四、Redis缓…

2024年大数据与艺术设计国际学术会议(ICBDAD 2024)

2024年大数据与艺术设计国际学术会议(ICBDAD 2024) 2024 International Conference on Big Data and Art Design 一、【会议简介】 2024年大数据与艺术设计国际学术会议&#xff0c;听起来好像两个完全不相关的领域&#xff0c;却意外地融合在了一起。 在这个会议上&#xff0c…

el-carousel走马灯页数回到第一页

我的走马灯是在一个弹窗里,包裹着一些button,切换到下一页时 关闭弹窗再打开弹窗还显示的是上次第二页位置 领导很不满意 1. 2.写在你打开弹窗或者关闭弹窗的位置 this.$refs.carousel && (this.$refs.carousel.activeIndex 0); 解释一下: this.$refs.carousel: thi…

开源数据可视化大屏对接表单数据实践!

如果你需要一个表单系统&#xff0c;进行数据收集&#xff1b;可以使用tduck填鸭进行私有化部署&#xff0c;进行表单制作&#xff0c;完成数据收集。 在实际业务中&#xff0c;往往需要将收集的数据进行展示或分析&#xff1b;此时就可以使用表单数据推送到TReport中&#xf…

华为eNSP中型企业局域网网络规划设计(下)

→b站传送门&#xff0c;感谢大佬← →华为eNSP中型企业局域网网络规划设计&#xff08;上&#xff09;← →拓扑图传送门&#xff0c;可以自己配置着玩← 配置ospf AR3 [AR3]ospf 1 router-id 3.3.3.3 //出口默认路由 [AR3-ospf-1]default-route-advertise always #area…

R语言数据探索与分析-中国GDP回归分析与预测

首先读取数据&#xff1a; 将GDP列转换为常规数字格式 # 可视化GDP数据 # 查看数据结构 # 确保数据类型是正确的 第一张图片展示了中国2002年到2021年间的GDP增长趋势&#xff0c;这是一个时间序列图&#xff0c;其中横轴表示年份&#xff0c;纵轴表示GDP&#xff08;单位未…

OpenAI 高管:一年后,你会觉得现在的 ChatGPT 像笑话一样糟糕|TodayAI

OpenAI 的首席运营官 Brad Lightcap 表示&#xff0c;一年后&#xff0c;你会觉得现在的 ChatGPT 像笑话一样糟糕。未来的 ChatGPT 版本将会有重大升级。他还讨论了 AI 取代人类工作和对电网的压力的可能性。 虽然我们不知道 OpenAI 何时会推出 GPT-5&#xff0c;但公司高管已…

三年软件测试经验遭遇求职困境?揭秘求职市场的隐藏陷阱

1.个人背景 小李&#xff0c;我的一位朋友&#xff0c;拥有三年多的软件测试工作经验。他本科毕业后便投身于测试行业&#xff0c;熟练掌握Python编程&#xff0c;能够编写自动化测试脚本&#xff0c;并且熟悉Selenium和性能测试。然而&#xff0c;尽管他具备这些技能和经验&am…

Zabbix+Grafana-常见报错及异常处理方式记录

文章目录 Zabbix安装篇Zabbix Web页面连接数据库失败 Zabbix使用篇中文显示不全 Zabbix报警篇新建的用户&#xff0c;配置报警后&#xff0c;无法收到报警 Grafana安装篇Windows系统安装时&#xff0c;添加zabbix报错&#xff1a;An error occurred within the plugin Zabbix安…

社交媒体数据恢复:多闪

社交软件多闪是一款深受用户喜爱的社交应用&#xff0c;用于与朋友、家人保持联系。有时&#xff0c;多闪软件的聊天记录可能会丢失或被删除&#xff0c;这时用户需要进行数据恢复。本文将详细介绍多闪软件聊天记录的恢复过程。 一、多闪软件聊天记录恢复方法 从手机备份中恢…

【每日刷题】Day34

【每日刷题】Day34 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1047. 删除字符串中的所有相邻重复项 - 力扣&#xff08;LeetCode&#xff09; 2. 1475. 商品折扣后…

Rust开发工具有哪些?

目录 一、JetBrains公司的RustRover​编辑 二、微软公司的Visual Studio Code 三、Rust编译工具 一、JetBrains公司的RustRover RustRover是由JetBrains开发的一款专为Rust开发量身定制的新兴IDE&#xff0c;目前还处于早期访问阶段。它支持Rust、Cargo、TOML、Web和数据库等…

Unity如何使用adb工具安装APK

1、下载adb工具 SDK 平台工具版本说明 | Android Studio | Android Developers (google.cn) 2、配置环境变量 把platform-tools的路径添加进去就行 打开cmd&#xff0c;输入adb&#xff0c;即可查看版本信息 3、使用数据线连接设备&#xff0c;查看设备信息&#xff08;…