栈与队列OJ题【括号适配问题】【用队列实现栈】【用栈实现队列】【设计循环队列】

news2024/11/23 13:09:01

 一.有效的括号

​​​OJ链接

 这一道题我们就可以用栈来解决:

不了解栈的可以看我的上一篇博客。

typedef char STDataType;
//用数组来实现栈
typedef struct stack
{
	STDataType* a;
	int capacity;
	int top;
}ST;
void STInit(ST* pst)
{
	assert(pst);

	pst->a = NULL;
    pst->capacity = 0;
	//在后面往栈里面放元素的时候,最后的top是在栈顶的下一个位置
	pst->top = 0;
	//如果是-1,那么代表的就是下标
	//pst->top = -1;
}
void STDestory(ST* pst)
{
	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}
//入栈
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)//如果top的数值刚好等于栈的总容量,那么栈就是满了,包含为0的情况。
	{
		int newcapacity = pst->capacity == 0 ? 4 : pst->capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, sizeof(STDataType) * newcapacity);//用realloc改变我们申请动态存储空间的大小
		if (tmp == NULL)
		{
			perror("STPush::relloc");
			return;
		}
		pst->a = tmp;//成功改变了空间,就赋值给栈里的a。
	
		pst->capacity = newcapacity;//总体的容量也随之变化。

	}
	pst->a[pst->top] = x;
	pst->top++;//因为我们的top的栈顶的位置,所以在每一次赋值完毕后就递增。
}

//出栈
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}

//读取栈顶元素
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top-1];
}

//判空
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
//获取数据个数
int STSize(ST* pst)
{
	assert(pst);
	return pst->top;
}
bool isValid(char* s) 
{
    ST p;
    STInit(&p);//初始化栈
    int sz=strlen(s);//求出栈里的总元素
    for(int i=0;i<sz;i++)
    {
        if(s[i]=='('||s[i]=='['||s[i]=='{')
        {
            STPush(&p,s[i]);//如果与三种左括号匹配上了,就入栈
        }
        else//否则就是右括号的情况
        {
            if(STEmpty(&p))//这里有一个判空,目的就是我们的栈里必须要有左括号,不然我们已经进入else了,这里就只有可能是右括号,而这个右括号就不可能与左括号匹配上了
            {
                STDestory(&p);//如果进入了这个if语句,就说明不可能匹配上了
                return false;
            }
            char top=STTop(&p);//取出都是左括号栈里的头栈元素
            STPop(&p);
            if((top=='('&&s[i]!=')')
            ||(top=='['&&s[i]!=']')
            ||(top=='{'&&s[i]!='}'))
            {
                STDestory(&p);//这个if语句就是,虽然栈里有左括号,同时也匹配到了右括号,但是括号直接不匹配的问题
                return false;
            }
        }
    }
    bool ret=STEmpty(&p);//如果栈为空了,就是说所以的括号都匹配上了
    STDestory(&p);
    return ret;
}

这个题主要需要注意的地方就是我们入栈入的都是左括号,右括号就单独的一个一个的与栈里的左括号相匹配。总的来说不算难,就是让我们了解一下栈的应用。

二.用队列实现栈

OJ链接

 简单的说呢,就是我们使用队列的先进先出的特点来完成栈的先进后出的特点。对于队列不熟悉的朋友,也可以看我们上一篇博客。

//先来完成队列的实现
typedef int QDataType;
typedef struct QNode//我们用链表来实现队列
{
	QDataType val;
	struct QNode* next;
}QNode;
typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;
//队列初始化
void QInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
//队列的销毁
void QDestroy(Queue* pq)
{
	assert(pq);
	QNode* pcur = pq->phead;
	while (pcur)//遍历整个链表
	{
		QNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->phead = pq->ptail = NULL;
	pq->size = 0;
}
//入队
void QPush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("newnode");
		return;
	}
	newnode->next = NULL;
	newnode->val = x;
	if (pq->size == 0)//链表没有节点
	{
		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = pq->ptail->next;
	}
	pq->size++;//用size记录链表节点的个数,也就是队列里元素的个数
}
//出队列
void QPop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);//出队列之前队列里一定要有元素

	if (pq->phead == pq->ptail)//一个节点
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}
	else//多个节点
	{
		QNode* pcur = pq->phead->next;
		free(pq->phead);
		pq->phead = pcur;
	}
	pq->size--;//出一个,减一个
}
//取队头元素
QDataType QTop(Queue* pq)
{
	assert(pq);
	assert(pq->phead);
	return pq->phead->val;
}
//取队尾元素
QDataType QBack(Queue* pq)
{
	assert(pq);
	assert(pq->ptail);
	return pq->ptail->val;
}
//求队列里元素个数
int QSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}
//判空
bool QEmpty(Queue* pq)
{
	assert(pq);
	return pq->size == 0;
}

//开始写题
typedef struct {
	Queue q1;
	Queue q2;
} MyStack;//创建两个队列

//初始化栈
MyStack* myStackCreate()
{
	MyStack* pst = (MyStack*)malloc(sizeof(MyStack));
	QInit(&(pst->q1));
	QInit(&(pst->q2));//因为pst->q是队列,所以调用上面写的初始化队列的函数
	return pst;
}
//入栈
void myStackPush(MyStack* obj, int x) {
	if (!QEmpty(&(obj->q1)))//这里的入队列,我们入的队列是为空的那一个
	{
		QPush(&(obj->q1), x);
	}
	else
	{
		QPush(&(obj->q2), x);
	}
}
//出栈
int myStackPop(MyStack* obj)
{
    //假设法,创建两个新的队列,一个为空队列,一个不为空
	Queue* empty = &(obj->q1);
	Queue* noempty = &(obj->q2);

	if (!QEmpty(&(obj->q1)))
	{
		empty = &(obj->q2);
		noempty = &(obj->q1);
	}
    //假设法完成后我们就只需要对empty和noempty进行改变
	while (QSize(noempty) > 1)//这里的循环和大于1的意思是,我们要把非空的队列入到为空的队列里,但是要剩余一个元素。
	{
		QPush(empty, QTop(noempty));
		QPop(noempty);
	}
	int top = QTop(noempty);//上面我们剩余的那个元素就是作为栈先进后出的那个元素(后面再单独解释一下)
	QPop(noempty);
	return top;
}
//提取栈顶元素
int myStackTop(MyStack* obj) {
	if (!QEmpty(&(obj->q1)))
	{
		return QBack(&(obj->q1));//队尾实际上就是栈顶
	}
	else
	{
		return QBack(&(obj->q2));
	}
}
//判空
bool myStackEmpty(MyStack* obj) {
	return QEmpty(&(obj->q1)) && QEmpty(&(obj->q2));
}

void myStackFree(MyStack* obj) {
	QDestroy(&(obj->q1));
	QDestroy(&(obj->q2));

	free(obj);
}

不能理解的可以看一下图:

相对于队列来说,如果要实现先进后出的特点,就要不断的往空队列移动元素,且移动的时候一定要保留一个,这个保留的元素相对于栈来说,就是我们要出栈的第一个元素。 

三.用栈实现队列

OJ链接

同样的,解决这道题,依然要先实现一下栈。

typedef int SLTDataType;
typedef struct Stack
{
	SLTDataType* a;
	int top;
	int capacity;
}ST;
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->capacity = 0;
	//top的位置在栈顶的后面的一个位置
	pst->top = 0;
}
void STDestory(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->capacity = pst->top = 0;
}
void STPush(ST* pst, SLTDataType x)
{
	assert(pst);
	if (pst->top == pst->capacity)
	{
		int newcapacity = pst->capacity == 0 ? 4 : 2 * pst->capacity;
		SLTDataType* tmp = (SLTDataType*)realloc(pst->a, newcapacity * sizeof(SLTDataType));
		if (tmp == NULL)
		{
			perror("STPush::realloc");
			return;
		}
		pst->a = tmp;
		pst->capacity = newcapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	pst->top--;
}
SLTDataType STTop(ST* pst)
{
	assert(pst);
	assert(pst->top > 0);
	return pst->a[pst->top - 1];
}
bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}

//正式开始实现队列
typedef struct {
    ST pushst;
    ST popst;
} MyQueue;

//初始化队列
MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));

    STInit(&obj->pushst);
    STInit(&obj->popst);
    return obj;
}
//入队
void myQueuePush(MyQueue* obj, int x) {
    STPush(&obj->pushst,x);//把需要入队的元素全部放在pushst里
}
//出队+删除
int myQueuePop(MyQueue* obj) {
    int front=myQueuePeek(obj);//调用下面的Peek函数,取出要出队的元素
    STPop(&obj->popst);然后删除
    return front;
}
//出队
int myQueuePeek(MyQueue* obj) {
    if(STEmpty(&obj->popst))//判断要popst是否为空
    {
        while(!STEmpty(&obj->pushst))//popst为空了并且pushst里面有元素
        {
            int top=STTop(&obj->pushst);//提取pushst里的元素
            STPush(&obj->popst,top);//放到popst里面
            STPop(&obj->pushst);//删除刚刚出去的pushst里的栈顶元素
        }
    }
    //这里的if语句和while语句的作用就是把pushst里的元素反过来放入到popst里面
    return STTop(&obj->popst);//最后在返回出栈的值,这个值就是我们要的先进先出的值
}
//判空
bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);
}
//释放
void myQueueFree(MyQueue* obj) {
    STDestory(&obj->pushst);
    STDestory(&obj->popst);
    free(obj);
}

这道题的思路就是先创建两个栈,一个专门用来入,一个专门用来出。要出栈的话就用popst。

等到我们把所有的元素都移动到popst里面的时候,再用栈的性质出栈,此时出来的元素的顺序将会是1,2,3,4。符合我们需要的特性,大家也可以试试入栈什么的。

四.设计循环队列

OJ链接




typedef struct {
    int* a;
    int head;
    int tail;//指向尾的下一个
    int k;//k是队列里所有的元素个数的总数
} MyCircularQueue;


MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));

    obj->a = (int*)malloc(sizeof(int) * (k + 1));//多创建一个sizeof(int)
    obj->head = 0;
    obj->tail = 0;
    obj->k = k;
    return obj;
}
bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->tail == obj->head;//如果head==tail就说明没有元素
}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->tail + 1) % (obj->k + 1) == obj->head;//这里取了一个模,是为了避免tail在k位置时的情况
}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if (myCircularQueueIsFull(obj))//满了就不能入了
        return false;

    obj->a[obj->tail] = value;//tail指向的是队尾的下一个位置
    obj->tail++;//可以分为正常情况和tail在k位置上的情况

    obj->tail %= (obj->k + 1);//有这个主要是为了避免tail在K位置上的情况
    return true;
}

bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return false;

    obj->head++;//删除队头的元素,直接head++就行了
    obj->head %= (obj->k + 1);//跟tail在k位置是同一个道理
    return true;
}

int myCircularQueueFront(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    return obj->a[obj->head];//直接返回队头元素
}

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[(obj->tail + obj->k) % (obj->k + 1)];//因为tail指向的是队尾的下一个位置,所以要小心一下tail在k位置的情况
}


void myCircularQueueFree(MyCircularQueue* obj) {
    free(obj->a);
    free(obj);
}

这里需要单独解释一下一个东西:

int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj))
        return -1;
    else
        return obj->a[(obj->tail + obj->k) % (obj->k + 1)];//因为tail指向的是队尾的下一个位置,所以要小心一下tail在k位置的情况
}

注意我return的是什么:(obj->tail + obj->k) % (obj->k + 1)这个东西又可以写成(obj->tail-1 + obj->k+1) % (obj->k + 1)obj->tail-1加上一个obj->k + 1再取obj->k + 1的余。最后的结果不会有改变,但是多加了这一步就完美的避免了,tail在0位置的情况。如果直接return的是tail-1会存在访问越界的问题。

到这里这四个OJ题就结束了,感谢大家的观看,如有错误,还请多多指出。 

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

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

相关文章

STK12 RPO模块学习(2)

一、Coast RPO Sequence 这个序列运行卫星直到它达到了下面三个条件之一。 1&#xff09;截至时间。2)圈数到达了限制。3&#xff09;其他条件&#xff0c;比如近地点。 默认情况下&#xff0c;Astrogator使用“Earth HPOP Default v10”预报器。你能够修改呈其他修改器。下…

Mysql与Java连接----JDBC

前言: 当将Java与MySQL数据库连接时&#xff0c;JDBC&#xff08;Java Database Connectivity&#xff09;是一种重要的技术。JDBC允许Java应用程序通过标准的数据库访问方式与不同的关系型数据库进行通信&#xff0c;其中包括MySQL。通过使用JDBC&#xff0c;Java开发人员可以…

如何查看centos7是否安装nginx

要查看 CentOS 7 系统上是否安装了 Nginx&#xff0c;您可以使用多种方法来检查。以下是一些常见的方法&#xff1a; 通过 RPM 包管理器查询 在 CentOS 系统上&#xff0c;可以使用 RPM 包管理器来查询已安装的软件包。要查看是否安装了 Nginx&#xff0c;您可以在终端中运行以…

宁夏银川市起名专家的老师颜廷利:死神(死亡)并不可怕,可怕的是...

在中国优秀传统文化之中&#xff0c;汉语‘巳’字与‘四’同音&#xff0c;在阿拉伯数字里面&#xff0c;通常用‘4’来表示&#xff1b; 湖南长沙、四川成都、重庆、宁夏银川最靠谱最厉害的起名大师的老师颜廷利教授指出&#xff0c;作为汉语‘九’字&#xff0c;倘若是换一个…

【GD32】03 - EXTI外部中断

EXTI EXTI&#xff0c;全称External Interrupt/Event Controller&#xff0c;即外部中断/事件控制器&#xff0c;是微控制器中的一个重要组成部分。它主要用于管理来自外部设备的中断和事件请求。以下是关于EXTI的详细介绍&#xff1a; 功能概述&#xff1a; EXTI管理了控制器的…

非常简单的长期兼职,无脑复制粘贴,每天稳定200-300+

带货这个概念已经不再陌生&#xff0c;许多人对此产生了浓厚的兴趣。今天&#xff0c;我将向大家介绍一个百家号带货项目&#xff0c;这是我一位朋友正在操作的项目。最近与他交流后&#xff0c;我觉得这个项目具有相当的操作性。他并非只运营了一个账号&#xff0c;而是同时管…

经典权限五张表案例分析

文章目录 模块分析模块分析 描述五张表的关系重要知识讲解抽取成一个BaseServletSpringIOC思想(底层)实现代码IOC概述 SPI机制(为学习框架做思想和技术铺垫)SPI引入1. 标准/规范2. 具体的实现3. 调用 SPI介绍SPI练习JDBC4.0免注册驱动原理Servlet实现方式三 ServletContainerIn…

卷积神经网络边缘识别

为什卷积神经网络能够识别图片呢&#xff1f;是基于图片相似度比较&#xff0c;两张图片的点击越大说明两张图片越像&#xff0c;比如我们那狗胡子的图片去比较&#xff0c;如果相似度很高&#xff0c;就是认为这个动物更像狗。点积越大&#xff0c;图片越相似&#xff0c;这个…

量化交易包含些什么?

我们讲过许多关于量化交易的内容&#xff0c;但是量化交易具体可以做些什么&#xff1f;很多朋友都还不清楚&#xff0c;我们详细来探讨下&#xff01; 第一&#xff1a;什么是量化交易&#xff1f; 量化交易是一种利用先进的数学模型和计算机技术&#xff0c;从大量的历史数…

老黄终于不穿皮衣了,分享一个AI换装AI试衣软件!

用AI实现在线试衣&#xff0c;或者在线换装&#xff0c;这不是一个新概念&#xff0c;肯定有人这么想过&#xff0c;但并不是所有人能都能轻松做到啊&#xff01; 今天就来分享一个人人都可以实现的方法&#xff0c;而且是那种傻瓜式的不用付钱的那种&#xff0c;甚至可以把软件…

ONVIF系列一:ONVIF介绍

感谢博主OceanStar的学习笔记&#xff0c;ONVIF系列二和系列三中安装操作过程及代码实现参考了这位博主的博客。 ONVIF系列&#xff1a; ONVIF系列一&#xff1a;ONVIF介绍 ONVIF系列二&#xff1a;Ubuntu安装gSOAP、生成ONVIF代码框架 ONVIF系列三&#xff1a;ONVIF客户端实现…

3款常用的可视化工具Matplotlib、Seaborn和Pandas

大家好&#xff0c;Seaborn 是基于 Matplotlib 的扩展库&#xff0c;Pandas 的可视化功能同样也依赖于 Matplotlib。尽管二者都使用相同的底层图形库&#xff0c;但绘制图表的方法却各有千秋。本文将介绍各种柱状图的绘制&#xff0c;比较 Matplotlib、Pandas 和 Seaborn 在数据…

ONVIF系列三:ONVIF客户端实现

ONVIF系列&#xff1a; ONVIF系列一&#xff1a;ONVIF介绍 ONVIF系列二&#xff1a;Ubuntu安装gSOAP、生成ONVIF代码框架 ONVIF系列三&#xff1a;ONVIF客户端实现 在系列二中完成了在Ubuntu上安装gSOAP并生成ONVIF代码框架&#xff0c;接下来我们利用生成的框架实现ONVIF客户端…

探究NVMe SSD HMB应用场景与影响-<续>

如果需要采用HMB功能&#xff0c;需要SSD支持NVME协议且NVMe 1.2及以上版本。NVME协议中对HMB对应有2个关键参数&#xff1a; HMB建议值&#xff08;HMPRE&#xff09;&#xff1a;设定实际分配给HMB使用的主机内存容量&#xff0c;为设备提供最优性能的内存分配量。 HMB最小值…

冥想的时候怎么专注自己

冥想的时候怎么专注自己&#xff1f;我国传统的打坐养生功法&#xff0c;实际最早可追溯到五千年前的黄帝时代。   每天投资两个半小时的打坐&#xff0c;有上千年之久的功效。因为当你们打坐进入永恒时&#xff0c;时间停止了。这不只是两个半小时&#xff0c;而是百千万亿年…

Github上5个实用的ChatGPT仓库

ChatGPT是一款基于聊天场景的大模型AI&#xff0c;最近火出圈。 Chat表示聊天&#xff0c;GPT表示大模型算法&#xff0c;它通过生成式的人机对话功能&#xff0c;让使用者第一次有了AI机器人‘懂我‘的感觉&#xff0c;而不是Siri、小爱那种傻瓜式的语音服务。 ChatGPT不仅仅…

现货黄金白银行情走高带来的投资机会分析

当现货黄金和白银行情呈现出走高的态势时&#xff0c;这常常被投资者解读为一个潜在的投资机会。本文旨在分析在黄金白银价格上涨时的投资机会&#xff0c;并指出应对策略。 一、走高行情背后的机会 行情的上升&#xff0c;往往代表了市场在某种程度上的认可&#xff0c;无论这…

知乎知+广告推广该如何做?怎么收费?

知乎作为一个汇聚高质量用户群体的知识分享平台&#xff0c;成为了众多品牌和产品推广的优选之地。特别是知乎的“知”广告推广服务&#xff0c;以其精准定向、内容原生的特点&#xff0c;深受广告主青睐。 一、知乎知广告推广基础 1. 什么是知乎知&#xff1f; 知是知乎官方…

企业网站慎用免费SSL证书!OV证书才是首选

市面上有很多免费证书提供&#xff0c;免费的SSL证书更适用于个人博客、学生学习、测试等应用场景。如果您的网站是企业网站一定慎用免费的SSL证书&#xff0c;而是选择企业级的OV证书。 一&#xff0e;免费SSL证书的风险 1安全性&#xff1a;免费SSL证书通常只提供基本的加密…

全新Transformer模型:全球与局部双重突破!

DeepVisionary 每日深度学习前沿科技推送&顶会论文分享&#xff0c;与你一起了解前沿深度学习信息&#xff01; 引言&#xff1a;探索视觉变换器在对象重识别中的全局与局部特征 在对象重识别&#xff08;Re-ID&#xff09;的研究领域中&#xff0c;如何有效地从不同时间…