数据结构-栈队列OJ题

news2025/3/1 4:47:54

文章目录

  • 一、有效的括号
  • 二、用队列实现栈
  • 三、用栈实现队列
  • 四、设计循环队列

一、有效的括号

(链接:ValidParentheses)
在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述这道题用栈这种数据结构解决最好,因为栈有后进先出的性质。简单分析一下这道题:所给字符串不是空的也就是一定至少存在一个括号,而且字符串中只能含有’(‘、’)‘、’{‘、’}‘、’[‘、’]'这六种字符。我们要根据题目给的实例,运用栈来解决这道题:
在这里插入图片描述在这里插入图片描述

bool isValid(char* s) 
{
	ST st;
	STInit(&st);
	while (*s)
	{
		if (*s == '('
			|| *s == '['
			|| *s == '{')
		{
			STPush(&st, *s);
		}
		else
		{
			if (STEmpty(&st))
				return false;
			char top = STTop(&st);
			STPop(&st);
			if ((*s == ')' && top != '(')
				|| (*s == ']' && top != '[')
				|| (*s == '}' && top != '{'))
			{
				return false;
			}
		}
		s++;
	}
	char ret = STEmpty(&st);
	STDestroy(&st);
	return ret;
}

在这里插入图片描述

二、用队列实现栈

(链接:ImplementStackUsingQueues)
在这里插入图片描述在这里插入图片描述这道题是要用队列来实现栈,首先我们要了解到队列的性质是先进先出,而栈的性质是后进先出。那么就需要两个队列才能实现栈:
在这里插入图片描述在这里插入图片描述

//用两个队列来实现栈
typedef struct 
{
	Queue q1;
	Queue q2;
} MyStack;

MyStack* myStackCreate() 
{
	//为自主实现的栈结构动态开辟空间
	MyStack* st = (MyStack*)malloc(sizeof(MyStack));
	if (st == NULL)
	{
		exit(1);
	}
	//初始化两个队列
	QueueInit(&st->q1);
	QueueInit(&st->q2);

	return st;
}

void myStackPush(MyStack* obj, int x) 
{
	assert(obj != NULL);
	if (!QueueEmpty(&obj->q1))
	{
		QueuePush(&obj->q1, x);
	}
	else
	{
		QueuePush(&obj->q2, x);
	}
}

int myStackPop(MyStack* obj) 
{
	assert(obj != NULL);
	Queue* QEmpty = &obj->q1;
	Queue* QNonEmpty = &obj->q2;
	if (!QueueEmpty(&obj->q1))
	{
		QEmpty = &obj->q2;
		QNonEmpty = &obj->q1;
	}
	//将QNonEmpty队列的前size-1个数据倒入QEmpty队列中
	while (QueueSize(QNonEmpty) > 1)
	{
		QueuePush(QEmpty, QueueFront(QNonEmpty));
		QueuePop(QNonEmpty);
	}
	//记录下QNonEmpty队列的队头元素
	int top = QueueFront(QNonEmpty);
	//移除并返回队头元素(即模拟返回栈的栈顶元素)
	QueuePop(QNonEmpty);
	return top;
}

int myStackTop(MyStack* obj) 
{
	assert(obj != NULL);
	if (!QueueEmpty(&obj->q1))
	{
		return QueueBack(&obj->q1);
	}
	else
	{
		return QueueBack(&obj->q2);
	}
}

bool myStackEmpty(MyStack* obj) 
{
	assert(obj != NULL);

	return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2);
}

void myStackFree(MyStack* obj) 
{
	assert(obj != NULL);
	QueueDestroy(&obj->q1);
	QueueDestroy(&obj->q2);

	free(obj);
}

注意:上面是用两个队列来实现栈的数据结构,MyStack是匿名结构struct 的重命名。MyStack的成员不是队列指针,而是队列结构体变量,所以在myStackCreake中为MyStack动态开辟的空间大小是为两个队列结构体变量开辟的,这样做的好处是可以取q1和q2的地址,方便我们直接可以修改队列q1和q2。当然也可以在MyStack中定义两个队列指针,但这样做的话就要单独为q1和q2动态开辟空间:

typedef struct 
{
	Queue* q1;
	Queue* q2;
} MyStack;


MyStack* myStackCreate() 
{
	MyStack* st = (MyStack*)malloc(sizeof(MyStack));
	if (st == NULL)
	{
		exit(1);
	}
	st->q1 = (Queue*)malloc(sizeof(Queue));
	st->q2 = (Queue*)malloc(sizeof(Queue));

	QueueInit(st->q1);
	QueueInit(st->q2);

	return st;
}

第一种定义方式:
在这里插入图片描述第二种定义方式:
在这里插入图片描述注意:如果采用上面这种方式定义MyStack,则后面的函数都要做出相应的调整。在最后销毁自定义的栈时要先释放MyStack中的两个队列q1和q2,最后才销毁obj。因为q1和q2在后面通过QueuePush动态开辟了空间,则要通过obj才能找到q1和q2。所以要先销毁q1和q2,最后才释放obj。当然在做这题的基础上,要先实现一个队列才行。

三、用栈实现队列

(链接:ImplementQueueUsingStacks)
在这里插入图片描述在这里插入图片描述这道题就是上面一道题的兄弟题,要求我们用栈来实现队列。
在这里插入图片描述在这里插入图片描述

在这里插入图片描述

//用两个栈来实现队列
typedef struct
{
	ST pushst;
	ST popst;
}MyQueue;

MyQueue* myQueueCreate()
{
	MyQueue* q = (MyQueue*)malloc(sizeof(MyQueue));
	if (q == NULL)
	{
		perror("malloc fail!");
		return NULL;
	}

	STInit(&q->pushst);
	STInit(&q->popst);

	return q;
}

void myQueuePush(MyQueue* obj, int x)
{
	assert(obj != NULL);
	STPush(&obj->pushst, x);
}

int myQueuePeek(MyQueue* obj)
{
	assert(obj != NULL);
	//如果popst里没有数据,则从pushst里倒数据到popst中
	if (STEmpty(&obj->popst))
	{
		while (!STEmpty(&obj->pushst))
		{
			STPush(&obj->popst, STTop(&obj->pushst));
			STPop(&obj->pushst);
		}
	}
	//返回popst的栈顶元素(即模拟队列出队头数据)
	return STTop(&obj->popst);
}

int myQueuePop(MyQueue* obj)
{
	assert(obj != NULL);
	int front = myQueuePeek(obj);
	STPop(&obj->popst);

	return front;
}

bool myQueueEmpty(MyQueue* obj)
{
	assert(obj != NULL);

	return STEmpty(&obj->pushst) && STEmpty(&obj->popst);
}

void myQueueFree(MyQueue* obj)
{
	assert(obj != NULL);
	STDestroy(&obj->pushst);
	STDestroy(&obj->popst);

	free(obj);
}

与栈实现队列很相似,这里需要借助两个栈相互倒数据才能实现队列。注意:上面是先实现myQueuePeek函数,即返回队头的数据但不从队头出数据。而后的myQueuePop函数正好可以调用myQueuePeek函数来获取队头的数据,但myQueuePop函数不仅会返回队头数据,而且还会将队头的数据出队。

四、设计循环队列

(链接:DesignCircularQueue)
在这里插入图片描述在这里插入图片描述实际中还有一种特殊的队列叫循环队列,环形队列首尾相连成环,环形队列可以使用数组实现,也可以使用循环链表实现:
在这里插入图片描述💡思考:队列满的情况下,为什么Q.rear 不存储数据?
答:为了能使用 Q.rear == Q.front 来区别是队空还是队满,我们常常认为出现左图时的情况为队空,而出现右图的情况是队满。即: rear+1==front就表示队满。试想:如果(b)图中Q.rear位置再存储一个数据a6进去,那么Q.rear就会指向a1,此时Q.front也指向a1;由(a)图中一开始Q.front == Q.rear就表示队列为空,那么此时Q.front也等于Q.rear,这就矛盾了。所以循环队列中要留一个位置用来表示队满。

这里我们用数组来实现循环队列,但是队列就有一个问题:数组不像循环链表一样有首尾指针相连,但是我们选择数组是有一定好处的。当然链表也是可以实现循环队列的,要具体问题具体分析。那如何在数组中实现循环队列呢?看下图:
在这里插入图片描述当数组中rear到达下标为5的位置时说明队列满了,那么就要删除数据留出空间才能继续存储数据;删除一个数据就直接让front+1即可,因为是模拟队列的先进先出,所以要从队头出数据。此时再入数据就要从rear的位置入,问题是入了数据后,rear+1就出了数组的下标范围,我们应该让rear回到0下标的位置才能真正实现循环队列:(注意:k是题目要求存储的元素个数)
在这里插入图片描述当循环队列满了就要出数据后才能有空间继续入数据:
在这里插入图片描述出数据了此时队列就不满了,那就可以继续往里入数据了,问题是要怎么让rear回到下标为0的位置?

答案是:🍓rear = (rear+1)%(k+1)🍓
这是一个很妙的公式,通过这个公式就可以解决让rear回到0下标处的问题,让队列变成循环队列。

在这里插入图片描述

typedef struct 
{
	int* arr;
	int front;
	int rear;
	int k;
} MyCircularQueue;
MyCircularQueue* myCircularQueueCreate(int k) 
{
	MyCircularQueue* q = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
	if (q == NULL)
	{
		perror("malloc fail!");
		return NULL;
	}
	q->arr = (int*)malloc(sizeof(int)*(k + 1));
	if (q->arr == NULL)
	{
		perror("Arr malloc fail!");
		return NULL;
	}
	q->front = q->rear = 0;
	q->k = k;

	return q;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	return obj->front == obj->rear;
}
bool myCircularQueueIsFull(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	return (obj->rear + 1) % (obj->k + 1) == obj->front;
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
	assert(obj != NULL);
	if (myCircularQueueIsFull(obj))
	{
		return false;
	}

	obj->arr[obj->rear] = value;
	obj->rear = (obj->rear + 1) % (obj->k + 1);
	
	return true;
}
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	if (myCircularQueueIsEmpty(obj))
	{
		return false;
	}

	obj->front = (obj->front + 1) % (obj->k + 1);

	return true;
}
int myCircularQueueFront(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}

	return obj->arr[obj->front];
}
int myCircularQueueRear(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	if (myCircularQueueIsEmpty(obj))
	{
		return -1;
	}

	return obj->arr[(obj->rear + obj->k) % (obj->k + 1)];
}
void myCircularQueueFree(MyCircularQueue* obj) 
{
	assert(obj != NULL);
	free(obj->arr);
	free(obj);
}

不管是插入元素,还是移除元素都要提前判断队列是否满了或是空的。返回队头或是队尾的元素也是一样的,要提前判断队列是否为空。注意:上面myCircularQueueRear函数是用来返回队尾元素的。如果队列不为空,则队尾元素的下标可以表示为:

🔥(rear + k) % (k + 1)🔥
这也是一个很妙的公式,可以解决rear在下标为0的位置时,则队尾元素是在数组的末尾位置。

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

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

相关文章

MindAgent:基于大型语言模型的多智能体协作基础设施

2023-09-18 ,加州大学洛杉矶分校(UCLA)、微软研究院、斯坦福大学等机构共同创建的新型基础设施,目的在评估大型语言模型在游戏互动中的规划和协调能力。MindAgent通过CuisineWorld这一新的游戏场景和相关基准,调度多智…

近红外简单ROI分析matlab(NIRS_SPM)

本次笔记主要想验证上篇近红外分析是否正确,因为叠加平均有不同的计算方法,一种是直接将每个通道的5分钟实时长单独进行叠加平均,另一种是将通道划分为1分钟的片段,将感兴趣的通道数据进行对应叠加平均,得到一个总平均…

开发神器之cursor

文章目录 cursor简介主要特点 下载cursor页面的简单介绍切换大模型指定ai学习的文件指定特定的代码喂给ai创建项目框架文件 cursor简介 Cursor 是一款专为开发者设计的智能代码编辑器,集成了先进的 AI 技术,旨在提升编程效率。以下是其主要特点和功能&a…

电脑风扇声音大怎么办? 原因及解决方法

电脑风扇是电脑的重要组件之一,它的作用是为电脑的各个部件提供冷却,防止电脑过热。然而,有时候我们会发现电脑风扇的声音特别大,不仅影响我们的使用体验,也可能是电脑出现了一些问题。那么,电脑风扇声音大…

SpringBoot错误码国际化

先看测试效果: 1. 设置中文 2.设置英文 文件结构 1.中文和英文的错误消息配置 package com.ldj.mybatisflex.common;import lombok.Getter;/*** User: ldj* Date: 2025/1/12* Time: 17:50* Description: 异常消息枚举*/ Getter public enum ExceptionEnum {//…

软考高级5个资格、中级常考4个资格简介及难易程度排序

一、软考高级5个资格 01、网络规划设计师 资格简介:网络规划设计师要求考生具备全面的网络规划、设计、部署和管理能力;该资格考试适合那些在网络规划和设计方面具有较好理论基础和较丰富从业经验的人员参加。 02、系统分析师 资格简介:系统分…

如何通过 Apache Airflow 将数据导入 Elasticsearch

作者:来自 Elastic Andre Luiz 了解如何通过 Apache Airflow 将数据导入 Elasticsearch。 Apache Airflow Apache Airflow 是一个旨在创建、安排(schedule)和监控工作流的平台。它用于编排 ETL(Extract-Transform-Load&#xff0…

STM32 学习笔记【补充】(十)硬件I2C读写MPU6050

该系列为笔者在学习STM32过程(主线是江科大的视频)中的记录与发散思考。 初学难免有所纰漏、错误,还望大家不吝指正,感谢~ 一、I2C 外设简介 I2C(Inter-Integrated Circuit)是一种多主多从的串行通信协议…

QT信号槽 笔记

信号与槽就是QT中处理计算机外设响应的一种机制 比如敲击键盘、点击鼠标 // 举例: 代码: connect(ls,SIGNAL(sig_chifanla()),ww,SLOT(slot_quchifan())); connect(ls,SIGNAL(sig_chifanla()),zl,SLOT(slot_quchifan()));connect函数:这是…

【React】插槽渲染机制

目录 通过 children 属性结合条件渲染通过 children 和 slot 属性实现具名插槽通过 props 实现具名插槽 在 React 中,并没有直接类似于 Vue 中的“插槽”机制(slot)。但是,React 可以通过 props和 children 来实现类似插槽的功能…

openharmony电源管理子系统

电源管理子系统 简介目录使用说明相关仓 简介 电源管理子系统提供如下功能: 重启服务:系统重启和下电。系统电源管理服务:系统电源状态管理和休眠运行锁管理。显示相关的能耗调节:包括根据环境光调节背光亮度,和根…

数据库(中)11讲

用颜色、有否下划线对应! E-R图

图像去雾数据集的下载和预处理操作

前言 目前,因为要做对比实验,收集了一下去雾数据集,并且建立了一个数据集的预处理工程。 这是以前我写的一个小仓库,我决定还是把它用起来,下面将展示下载的路径和数据处理的方法。 下面的代码均可以在此找到。Auo…

STM32入门教程-示例程序(按键控制LED光敏传感器控制蜂鸣器)

1. LED Blink(闪烁) 代码主体包含:LED.c key.c main.c delay.c(延时防按键抖动) 程序代码如下(涉及RCC与GPIO两个外设): 1.使用RCC使能GPIO时钟 RCC_APB2PeriphClockC…

一本书揭秘程序员如何培养架构思维!

在程序员的职业规划中,成为软件架构师是一个非常有吸引力的选择。但是对于如何才能成为一名架构师,不少同学认为只要代码写得好,就能得到公司提拔,晋升为架构师。 还真不是这样的,如果不具备架构思维,即使…

Flink(十):DataStream API (七) 状态

1. 状态的定义 在 Apache Flink 中,状态(State) 是指在数据流处理过程中需要持久化和追踪的中间数据,它允许 Flink 在处理事件时保持上下文信息,从而支持复杂的流式计算任务,如聚合、窗口计算、联接等。状…

Vue2+OpenLayers实现点位拖拽功能(提供Gitee源码)

目录 一、案例截图 二、安装OpenLayers库 三、代码实现 3.1、初始化变量 3.2、创建一个点 3.3、将点添加到地图上 3.4、实现点位拖拽 3.5、完整代码 四、Gitee源码 一、案例截图 可以随意拖拽点位到你想要的位置 二、安装OpenLayers库 npm install ol 三、代码实现…

2024年博客之星年度评选—创作影响力评审入围名单公布

2024年博客之星活动地址https://www.csdn.net/blogstar2024 TOP 300 榜单排名 用户昵称博客主页 身份 认证 评分 原创 博文 评分 平均 质量分评分 互动数据评分 总分排名三掌柜666三掌柜666-CSDN博客1001002001005001wkd_007wkd_007-CSDN博客1001002001005002栗筝ihttps:/…

NVIDIA发布个人超算利器project digital,标志着ai元年的开启

上图NVIDIA公司创始人兼首席执行官 黄仁勋(Jensen Huang) 这些年被大家熟知的赛博朋克风格一直都是未来的代言词,可以承载人类记忆的芯片,甚至能独立思考的仿生人,现在,随着NVIDIA的project digital发布之后…

(一)afsim第三方库编译

注意:防止奇怪的问题,源码编译的路径最好不要有中文,请先检查各文件夹名 AFSIM版本 Version: 2.9 Plugin API Version: 11 软件环境 操作系统: Kylin V10 SP1 项目构建工具: cmake-3.26.0-linux-aarch6…