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

news2024/11/25 10:41:21

目录

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
    {
        //检查是否数量匹配,检查栈是否还有元素,为空返回非0
        if(StackEmpty(&ps))
        {
            StackDestroy(&ps);
            return false;
        }

        //左右括号比较,不相等返回fasle
        char 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;
    }
    else
    return 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;
  }
  else
  return 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.coloradmin.cn/o/1288740.html

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

相关文章

Retrofit嵌套请求与适配器

一、前言&#xff1a; 1. retrofit嵌套请求 在实际开发中&#xff0c;可能会存在&#xff1a;需要先请求A接口&#xff0c;在请求B接口的情况&#xff0c;比如进入“玩android”网页请求获取收藏文章列表&#xff0c;但是需要先登录拿到Cookie才能请求搜藏文章几口&am…

语义分割 LR-ASPP网络学习笔记 (附代码)

论文地址&#xff1a;https://arxiv.org/abs/1905.02244 代码地址&#xff1a;https://github.com/WZMIAOMIAO/deep-learning-for-image-processing/tree/master/pytorch_segmentation/lraspp 1.是什么&#xff1f; LR-ASPP是一个轻量级语义分割网络&#xff0c;它是在Mobil…

【最新版本教程】GPT4暂停升级也可硬升!

本教程亲测整个过程&#xff0c;没有问题。 步骤&#xff1a; 1、有自己的3.5账号&#xff08;没有的可以自己去注册&#xff0c;据说现在不用手机号了&#xff0c;方法自己查去&#xff09;&#xff1b; 2、解决国外银行卡的问题。国外银行卡开通&#xff08;https://bewild…

Kafka集群调优

一、前言 我们需要对4个规格的kafka能力进行探底&#xff0c;即其可以承载的最大吞吐&#xff1b;4个规格对应的单节点的配置如下&#xff1a; 标准版&#xff1a; 2C4G铂金版&#xff1a; 4C8G专业版&#xff1a; 8C16G企业版&#xff1a; 16C32G 另外&#xff0c;一般来讲…

Excel——多列合并成一列的4种方法

Excel怎么将多列内容合并成一列&#xff1f; 怎么将多个单元格的内容连接起来放在一个单元格里&#xff1f; 比如下图&#xff0c;要将B、C、D列的内容&#xff0c;合并成E列那样&#xff0c;该怎么做呢&#xff1f; △图1 本文中&#xff0c;高潜老师将给大家介绍 4种 将多…

制作一个RISC-V的操作系统五-RISC-V汇编语言编程二

文章目录 RISC-V汇编指令操作对象RISC-V汇编指令编码格式小端序的概念RISC-V汇编指令分类RISC-V汇编伪指令 RISC-V汇编指令操作对象 RV32I&#xff1a;RISC-V32位机器整数指令集 指令集分非特权指令集和特权指令集 XLEN&#xff1a;变量代表当前机器的字长&#xff08;32位 64…

「Verilog学习笔记」根据状态转移写状态机-三段式

专栏前言 本专栏的内容主要是记录本人学习Verilog过程中的一些知识点&#xff0c;刷题网站用的是牛客网 状态机可以分为Moore状态机和Mealy状态机。 Moore状态机&#xff1a;输出只由当前状态决定Mealy状态机&#xff1a;输出由当前状态和当前的输入共同决定。 三段式状态机是指…

项目实战之RabbitMQ重试机制进行消息补偿通知

&#x1f9d1;‍&#x1f4bb;作者名称&#xff1a;DaenCode &#x1f3a4;作者简介&#xff1a;啥技术都喜欢捣鼓捣鼓&#xff0c;喜欢分享技术、经验、生活。 &#x1f60e;人生感悟&#xff1a;尝尽人生百味&#xff0c;方知世间冷暖。 文章目录 &#x1f31f;架构图&#x…

软件设计之装饰模式

装饰模式把每个要装饰的功能放在单独的类中&#xff0c;并让这个类包装它所要装饰的对象&#xff0c;因此&#xff0c;当需要执行特殊行为时&#xff0c;客户代码就可以在运行时根据需要有选择地、按顺序地使用装饰功能包装对象。 案例&#xff1a;穿搭。衣柜有帽子、眼镜、…

Zabbix自动发现机制

Zabbix的自动发现机制 Zabbix客户端主动的和服务端联系&#xff0c;将自己的地址和端口发送服务端&#xff0c;实现自动添加监控主机&#xff0c;客户端是主动的一方缺点自定义网段中主机数量太多&#xff0c;等级耗时会很久&#xff0c;而且这个自动发现机制不是很稳定 Zabb…

二叉树的前序中序后序遍历

二叉树的前序中序后序遍历-含递归和迭代代码 前序(中左右)中序(左中右)后序(左右中) 前序(中左右) 对于二叉树中的任意一个节点&#xff0c;先打印该节点&#xff0c;然后是它的左子树&#xff0c;最后右子树 A-B-D-E-C-F //递归 const preorderTraversal (root) > {const…

【荣誉】科东软件荣获广州市软件行业协会双料大奖!

软件产业在数字经济中扮演着基础支撑的角色&#xff0c;对于优化产业结构、提高自主可控、赋能整体经济高质量发展具有关键作用。 近日&#xff0c;广州市软件行业第七届会员大会第三次会议成功召开&#xff01;此次会议旨在回顾过去一年的行业发展&#xff0c;展望未来的趋势和…

Linux文件结构与文件权限

基于centos了解Linux文件结构 了解一下文件类型 Linux采用的一切皆文件的思想&#xff0c;将硬件设备、软件等所有数据信息都以文件的形式呈现在用户面前&#xff0c;这就使得我们对计算机的管理更加方便。所以本篇文章会对Linux操作系统的文件结构和文件权限进行讲解。 首先…

新项目决定用 JDK 17了

大家好&#xff0c;我是风筝。 最近在调研 JDK 17&#xff0c;并且试着将之前的一个小项目升级了一下&#xff0c;在测试环境跑了一段时间。最终&#xff0c;决定了&#xff0c;新项目要采用 JDK 17 了。 JDK 1.8&#xff1a;“不是说好了&#xff0c;他发任他发&#xff0c;…

uni-app 设置tabBar的setTabBarBadge购物车/消息等角标

目录 一、效果二、代码实现二、全部代码1.index.vue2.cart.vue 三、真实案例参考最后 一、效果 二、代码实现 只要使用uni.setTabBarBadge和uni.removeTabBarBadge来进行对红点的设置和移除。 主要代码&#xff1a; //设置红点 uni.setTabBarBadge({index: 1, // 底部菜单栏…

urllib 的 get 请求和 post 请求(二)

目录 一、爬取网页、图片视频 二、请求对象的定制 三、get请求的urlencode方法 四、post 请求英文翻译 一、爬取网页、图片视频 目标&#xff1a;下载数据 知识点&#xff1a;urllib.request.urlretrieve()下载 使用urllib下载网页、图片和视频 下载网页&#xff1a; #…

【动态规划】LeetCode2111:使数组 K 递增的最少操作次数

作者推荐 [二分查找]LeetCode2040:两个有序数组的第 K 小乘积 本文涉及的基础知识点 二分查找算法合集 分组 动态规划 题目 给你一个下标从 0 开始包含 n 个正整数的数组 arr &#xff0c;和一个正整数 k 。 如果对于每个满足 k < i < n-1 的下标 i &#xff0c;都有…

项目进度已经落后了,项目经理该怎么办?

进度管理是项目管理的核心工作之一&#xff0c;通过可续的进度计划与控制管理&#xff0c;最终实现项目按照目标交付。 进度管理的两大核心工作&#xff1a;计划制定、过程管控。 项目管理过程中难免会遇到工作进度和计划不一致的情况&#xff0c;有效管理项目进度&#xff…

数字工厂时代,如何实现3D数据访问与发布、WEB大模型可视化?

Tech Soft 3D的HOOPS 3D CAD SDK为现代工厂工作流程奠定了基础&#xff0c;通过最快、最准确的CAD数据访问和动态3D可视化支持数字孪生、机器人仿真、设计、流程和规划、IIoT和操作辅助应用程序。 本文将和您详细探讨。如何利用HOOPS技术来增强您的应用程序。 HOOPS_HOOPS试…

INFINI Console 与华为鲲鹏完成产品兼容互认证

何为华为鲲鹏认证 华为鲲鹏认证是华为云围绕鲲鹏云服务&#xff08;含公有云、私有云、混合云、桌面云&#xff09;推出的一项合作伙伴计划&#xff0c;旨在为构建持续发展、合作共赢的鲲鹏生态圈&#xff0c;通过整合华为的技术、品牌资源&#xff0c;与合作伙伴共享商机和利…