栈的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;也是…

一个月速刷leetcodeHOT100 day02

今天的三道题 移动零 在双指针分组里的简单题 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 请注意 &#xff0c;必须在不复制数组的情况下原地对数组进行操作。 输入: nums [0,1,0,3,12] 输出: [1,3,12,0…

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;对各种操作实施…

MySQL表分区深入指南

MySQL的表分区功能允许将一个表的数据分散存储在多个物理子表中&#xff0c;但在逻辑上仍表现为一个单独的表。这可以帮助提高大型数据库表的查询和维护效率。本文将详细介绍如何在MySQL中实施和管理表分区&#xff0c;包括分区类型、创建分区表的步骤&#xff0c;以及分区表的…

Day_3

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

ES集群搭建、身份认证配置

虚机搭建 添加es用户 elasticsearch 默认不允许root用户启动&#xff0c;所以需要创建es用户 useradd elasticsearch passwd elasticsearch解压安装包 #解压es tar -xvzf elasticsearch-7.14.2-linux-x86_64.tar.gz将文件夹赋予es用户权限 #将文件夹赋予es用户权限 sudo c…

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

js中日期Date的使用

日常搬砖过程中,凡事使用到Date总是一知半解,每次都要查阅资料,今天有时间把这里整理一下,希望今天储备的知识,够以后使用就不用查阅资料了哈,现在开始吧 new Date() 使用它可以创建一个日期实例,并返回代表当前时间的字符串 接收参数有5种形式 无参数,新创建的 Date 对象代…

Qt绘制箭头

Qt绘制箭头 接收两个点作为向量的两端&#xff0c;用来确定箭头方向&#xff0c;额外添加参数&#xff0c;确定箭头是否和向量一致 然后使用fillPath填充箭头 QPainterPath arrowPath(const QPointF& p1, const QPointF& p2, int direct 0, int endIndex 0, int ar…

智慧监控 高效运维

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

量化交易的优势和QMT、Ptrade开通流程

量化交易没有一个精确的定义&#xff0c;广义上可以认为&#xff0c;凡是借助于数学模型和计算机实现的交易方法都可以称为量化交易。 量化交易的基本原理是通过计算机方法对海量的股票市场历史数据进行分析&#xff0c;总结出一些能够带来持续收益的交易因子。根据这些因子编…

Tomcat添加服务以及设置开机自启

下载地址连接 Index of /dist/tomcat👓 注意点:不要出现中文路径 #环境变量CATALINA_HOME=D:\apache-tomcat-7.0.62 TOMCAT_HOME=D:\apache-tomcat-7.0.62 JAVA_HOME=D:\tool\jdk1.8.0_111 PATH=%CATALINA_HOME%\bin;%CATALINA_HOME%\lib;%CATALINA_HOME%\lib;D:\tool\jdk1.…

将数据结构和流程可视化——E-R图和DFD图

通过程序处理数据时&#xff0c;不仅可以将数据保存到文件&#xff0c;还可以将其保存到数据库中。在数据库中数据是以表格形式保存的&#xff0c;但并不是只有一个表格&#xff0c;而是多个表格进行保存&#xff0c;因此数据更易于管理&#xff0c;即使变更数据&#xff0c;也…

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缓…

【ITK配准】第七期 尺度(Metric)-基匹配Metric

很高兴在雪易的CSDN遇见你 VTK技术爱好者 QQ:870202403 公众号:VTK忠粉 前言 本文分享ITK中的基匹配Metric,即itk::MatchCardinalityImageToImageMetric ,希望对各位小伙伴有所帮助! 感谢各位小伙伴的点赞+关注,小易会继续努力分享,一起进步! 你的点赞就是我…

【算法刷题day45】卡码网:70. 爬楼梯(进阶版);Leetcode:322. 零钱兑换、279. 完全平方数

文章目录 Leetcode 70. 爬楼梯&#xff08;进阶版&#xff09;解题思路代码总结 Leetcode 322. 零钱兑换解题思路代码总结 Leetcode 279. 完全平方数解题思路代码总结 草稿图网站 java的Deque Leetcode 70. 爬楼梯&#xff08;进阶版&#xff09; 题目&#xff1a;70. 爬楼梯&…

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

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

力士乐触摸屏维修VCP11.2DWN-003-NN-NN-PW工业液晶屏幕电脑主机深圳捷达工控维修

扩展的性能、高系统可用性和多种配置选项 借助 lndraControl 系列工业 PC 和显示器&#xff0c;实现高性能和高灵活性。 lndraControl PR 性能分级盒采用最新的 Intel ATOM 和 Core-i 处理器&#xff0c;可与 lndraControl DR 多点触控显示屏集成&#xff0c;连接距离可达 100M…