数据结构初阶--栈和队列OJ题

news2024/12/23 10:44:42

目录

  • 前言
  • 有效的括号
    • 思路分析
    • 代码实现
  • 用队列实现栈
    • 思路分析
    • 代码实现
  • 用栈实现队列
    • 思路分析
    • 代码实现
  • 设计循环队列
    • 思路分析
    • 代码实现

前言

本篇文章将对部分栈和队列综合运用题进行讲解,以对栈和队列有一个更深层次的理解。

有效的括号

先来看题

思路分析

这里我们采取的方法是将左括号入栈,右括号与栈顶的括号进行比较,如果不匹配,则返回false,当所有括号匹配完则返回true
但是,这里最后几个测试用例会设置各种各样的坑。
1. 我们在每次取栈顶元素时首先要判空,如果为空则返回false。
2. 在将所有的左括号匹配完后,还需对栈判空,如果不为空,说明还有右括号没有匹配,则返回false。
3. 在取出栈顶元素后别忘了将其删除(pop)。

代码实现

由于代码是c语言版本,必须要自己创建一个栈,所以代码可能会有些冗长。

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int Capacity;
}ST;


bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->Capacity == pst->top)
	{
		int newCapacity = pst->Capacity == 0 ? 4 : pst->Capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
		}
		pst->a = tmp;
		pst->Capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}


bool isValid(char * s){
    ST pst;
    STInit(&pst);
    int len=strlen(s);
    if(len%2==1)
    return false;
    for(int i=0;i<len;i++)
    {
        if(s[i]=='('||s[i]=='['||s[i]=='{')
        {
            STPush(&pst,s[i]);
        }
        else
        {
            if(s[i]==')')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='(')
                    return false;
                }
                else
                return false;
            }
            if(s[i]==']')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='[')
                    return false;
                }
                else
                return false;
            }
            if(s[i]=='}')
            {
                if(!STEmpty(&pst))
                {
                    char ch = STTop(&pst);
                    STPop(&pst);
                    if(ch!='{')
                    return false;
                }
                else
                return false;
            }
        }
    }
    if(!STEmpty(&pst))
    {
        return false;
    }
    return true;
}

用队列实现栈

先来看题

思路分析

这道题的主体思路就是用两个队列queue1、queue2,先将元素存入queue2,要出栈时将queue2中的元素出队列存入queue1中,剩下一个queue2得队尾元素用于出栈即可。
若不是第一次出栈,则将queue1中的元素出队列存入queue2中,剩下一个queue1得队尾元素用于出栈即可

代码实现

typedef int QDataType;
typedef struct QueueNode
{
	struct QueueNode* next;
	QDataType data;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->size == 0;
}
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->phead = pq->ptail = NULL;

	pq->size = 0;
}
void QueueDestroy(Queue* pq)
{
	assert(pq);
	//先删节点
	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	//再删头尾指针
	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->ptail == NULL)
	{
		assert(pq->phead == NULL);

		pq->phead = pq->ptail = newnode;
	}
	else
	{
		pq->ptail->next = newnode;
		pq->ptail = newnode;
	}
	pq->size++;
}
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	if (pq->phead->next == NULL)
	{
		free(pq->phead);
		pq->phead = pq->ptail = NULL;
	}

	else
	{
		QNode* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}
QDataType QueueFront(Queue* pq)
{
	assert(pq);
    assert(!QueueEmpty(pq));

	return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));

	return pq->ptail->data;
}
int QueueSize(Queue* pq)
{
	assert(pq);

	return pq->size;
}


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


MyStack* myStackCreate() {
    MyStack* obj=(MyStack*)malloc(sizeof(MyStack));
    if(obj==NULL)
    {
        perror("malloc fail\n");

    }
    QueueInit(&obj->q1);
    QueueInit(&obj->q2);

    return obj;
}

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

int myStackPop(MyStack* obj) {
    Queue* EmptyQueue=&obj->q1;
    Queue* NonEmptyQueue=&obj->q2;
    if(!QueueEmpty(&obj->q1))
    {
        EmptyQueue=&obj->q2;
        NonEmptyQueue=&obj->q1;
    }
    while(QueueSize(NonEmptyQueue)>1)
    {
        QueuePush(EmptyQueue,QueueFront(NonEmptyQueue));
        QueuePop(NonEmptyQueue);
    }
    int top=QueueFront(NonEmptyQueue);
    QueuePop(NonEmptyQueue);

    return top;



}

int myStackTop(MyStack* obj) {
    if(QueueEmpty(&obj->q1))
    return QueueBack(&obj->q2);
    else
    return QueueBack(&obj->q1);


}

bool myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1)
      && QueueEmpty(&obj->q2);

}

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

    free(obj);

}

用栈实现队列

先看题

思路分析

主体思路就是将其中一个栈用于放入,另一个栈用于取出,每次出栈元素顺序都会翻转,出栈一次正好和队列顺序相匹配。

注意: 在返回队列开头元素时,应先判断用于取出的栈是否为空,若不为空,则直接将栈顶元素取出即可,若为空,则将用于放入的栈元素全部放入用于取出的栈,再返回栈顶元素。

代码实现

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int Capacity;
}ST;

bool STEmpty(ST* pst)
{
	assert(pst);
	return pst->top == 0;
}
void STInit(ST* pst)
{
	assert(pst);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STDestroy(ST* pst)
{
	assert(pst);
	free(pst->a);
	pst->a = NULL;
	pst->Capacity = 0;
	pst->top = 0;
}
void STPush(ST* pst, STDataType x)
{
	assert(pst);
	if (pst->Capacity == pst->top)
	{
		int newCapacity = pst->Capacity == 0 ? 4 : pst->Capacity * 2;
		STDataType* tmp = (STDataType*)realloc(pst->a, newCapacity * sizeof(STDataType));
		if (tmp == NULL)
		{
			perror("realloc fail");
		}
		pst->a = tmp;
		pst->Capacity = newCapacity;
	}
	pst->a[pst->top] = x;
	pst->top++;
}
void STPop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	pst->top--;
}
STDataType STTop(ST* pst)
{
	assert(pst);
	assert(!STEmpty(pst));

	return pst->a[pst->top - 1];
}
int STSize(ST* pst)
{
	assert(pst);

	return pst->top;
}


typedef struct {
    ST pushst;//放
    ST popst;//取
} MyQueue;


MyQueue* myQueueCreate() {
    MyQueue* obj=(MyQueue*)malloc(sizeof(MyQueue));
    if(obj==NULL)
    {
        perror("malloc fail\n");
        return NULL;
    }
    STInit(&obj->pushst);
    STInit(&obj->popst);

    return obj;

}

void myQueuePush(MyQueue* obj, int x) {
    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))
    {
        while(!STEmpty(&obj->pushst))
        {
            STPush(&obj->popst,STTop(&obj->pushst));
            STPop(&obj->pushst);
        }
    }

    return STTop(&obj->popst);

}

bool myQueueEmpty(MyQueue* obj) {
    return STEmpty(&obj->pushst)&&STEmpty(&obj->popst);

}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushst);
    STDestroy(&obj->popst);

    free(obj);

}

设计循环队列

先看题

思路分析

我们首先要考虑一个问题,如何区分满和空?
当队列满或空时,rear和front的下标相同

为了解决这个问题,我们考虑增加一个空间,不存储任何数据,如果rear+1等于front则满
在解决这个问题以后,我们再来看其他操作,
假设循环队列存储5个数据,则我们先开辟六个空间。

此时rear==front为空。

那么此时我们该如何判满呢?
比如当front为0,rear为5,此时队列满了,又或者是当rear是2,front是3,此时又满了,似乎只要满足rear+1=front即可,但我们仔细想这样一个问题,数组又没法像链表那样,可以直接从尾部找到头部,所以数组下标如果超出了存储空间,我们取模即可让下标又从头开始往后走,

如何插入数据呢?
这个很简单,只需将值放入下标为rear的位置,再将rear++即可,注意如果超出范围,则需取模,由于小于最大范围取模还是等于它本身,我们干脆每次都取模

如何删除数据?
我们只需将front++即可,同样要取模

如何获取队首元素?
很简单,直接返回下标为front的元素即可。

如何获取队尾元素?
队尾元素即为rear前一个下标对应的元素,但存在特殊情况,假设此时rear指向了5,我们应该返回4,如果指向0,应该返回5,但5-1=4,0-1很显然不等于5,所以我们还是考虑用取模的方法,这里直接给出公式了
(rear+k)%(k+1).

代码实现

typedef struct {
    int front;
    int rear;
    int k;
    int* a;

} MyCircularQueue;

MyCircularQueue* myCircularQueueCreate(int k) {
    MyCircularQueue* obj=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    obj->a=(int*)malloc(sizeof(int)*(k+1));
    obj->front=obj->rear=0;
    obj->k=k;

    return obj;

}

bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    return obj->front==obj->rear;

}

bool myCircularQueueIsFull(MyCircularQueue* obj) {
    return (obj->rear+1)%(obj->k+1)==obj->front;

}

bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    if(myCircularQueueIsFull(obj))
    return false;
    obj->a[obj->rear]=value;
    obj->rear++;

    obj->rear%=(obj->k+1);
    return true;

}

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

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

}

int myCircularQueueFront(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;

    return obj->a[obj->front];

}

int myCircularQueueRear(MyCircularQueue* obj) {
    if(myCircularQueueIsEmpty(obj))
    return -1;

    return obj->a[(obj->rear+obj->k)%(obj->k+1)];

}


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

}

以上就是本篇文章全部内容,如有出入,欢迎指正。

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

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

相关文章

优秀的流程图应该怎么设计呢?

优秀的流程图应该怎么绘制呢&#xff1f; 本文将带大家学习优秀流程图的绘制要点和技巧&#xff0c;以及讲解流程图与UML活动图、BPMN图之间的关系和区别。 1、认识流程图 流程图简单讲就是用图描述流程&#xff0c;这种流程可以是一种有先后顺序的操作组成&#xff0c;可以…

2024王道数据结构考研丨第六篇:查找、排序

到此&#xff0c;2024王道数据结构考研笔记专栏“基础知识”部分已更新完毕&#xff0c;其他内容持续更新中&#xff0c;欢迎 点此 订阅&#xff0c;共同交流学习… 文章目录 第七章 查找7.1查找表相关概念 第八章 排序8.1排序的基本概念8.2 插入排序8.2.1直接插入排序8.2.2折半…

使用Maven管理项目、导入依赖、测试打包项目、常用依赖

使用Maven管理项目 文章目录 使用Maven管理项目Maven项目结构Maven依赖导入Maven依赖作用域Maven可选依赖Maven排除依赖Maven继承关系Maven常用命令Maven测试项目Maven打包项目 Maven 翻译为"专家"、“内行”&#xff0c;是 Apache 下的一个纯 Java 开发的开源项目。…

hive函数03

自定义函数 Hive 自带了一些函数&#xff0c;比如&#xff1a;max/min等&#xff0c;但是数量有限&#xff0c;自己可以通过自定义UDF来方便的扩展。 在企业中处理数据的时候&#xff0c;对于敏感数据往往需要进行脱敏处理。比如手机号。我们常见的处理方式是将手机号中间4位…

MySQL表设计原则

前言 这里简单整理一些常用的数据库表设计原则以及常用字段的使用范围。 表的设计准则 1、命名规范 表名、字段名必须使用小写字母或者数字&#xff0c;禁止使用数字开头&#xff0c;禁止使用拼音&#xff0c;并且一般不使用英文缩写。主键索引名为 pk_字段名&#xff1b;唯…

SSL/TLS认证握手过程

一: SSL/TLS介绍 什么是SSL,什么是TLS呢&#xff1f;官话说SSL是安全套接层(secure sockets layer)&#xff0c;TLS是SSL的继任者&#xff0c;叫传输层安全(transport layer security)。说白点&#xff0c;就是在明文的上层和TCP层之间加上一层加密&#xff0c;这样就保证上层信…

ACP(MaxCompute篇)-MaxCompute开发工具

创建MaxCompute项目 第一种创建项目方式 1.知道MaxCompute服务。 2.创建项目。 3.创建成功。 第二种创建项目的方式 1.进入DataWorks控制台。 2.创建工作空间。 3.创建的类型。 4.创建计算方式。 5.自定义选择。 6.创建成功。 MaxCompute开发工具简介 Odpscmd 安装配置 下…

java boot项目认识一下三种格式的配置文件

之前我们在 application.properties 中写了很多配置 但boot并不是只有这种配置方式 boot提供了三种配置方式给我们 话不多说 直接上代码 我们先将 resources下的 application.properties给他干掉 然后在下面创建一个 application.yml 在下面编写代码如下 server:port: 81这…

Hystrix底层核心原理

1、Hystrix资源隔离技术 hystrix github 官方文档&#xff1a;Home Netflix/Hystrix Wiki GitHub hystrix可以完成隔离、限流、熔断、降级这些常用保护功能。 hystrix的隔离分为线程池隔离和信号量隔离 1.1、信号量隔离 信号量隔离就是hystrix的限流功能。虽然名字叫隔离…

企业应该如何选择一个靠谱的软件测试供应商?

人们的生活越来越离不开软件产品&#xff0c;随着选择越多&#xff0c;产品质量愈发重要&#xff0c;因此企业选择一个靠谱的软件测试供应商是一项关键任务&#xff0c;因为测试结果将直接影响到产品的质量、用户的体验和公司的声誉。以下是一些选择靠谱的软件测试供应商的技巧…

C++常用的支持中文的GUI库Qt 6之二:项目的结构、资源文件的使用

C常用的支持中文的GUI库Qt 6之二&#xff1a;项目的结构、资源文件的使用 上一篇Qt 6的下载、安装与简单使用https://mp.csdn.net/mp_blog/creation/editor/130730203&#xff0c;本文介绍Qt 6的项目的结构、资源文件的使用与发布。 基础 这一部分&#xff0c;初学时不明白是…

交通 | 考虑网络效应的共享出行差异化定价

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/210182/ 编者按&#xff1a; 本文考虑了单程式共享汽车的定价问题&#xff0c;在考虑顾客需求网络效应以及实现影响的场景下&#xff0c;根据空间以及时间确定汽车租赁的单价以实现系统利润最大化。 1.引言 在过…

【C++】unordered_map unordered_set 练习题

文章目录 unordered系列关联式容器unordered_mapunordered_map的文档介绍unordered_map的构造接口使用: unordered_multimapunorder_map&&unorder_multimap对比:unordered_setunordered_set的文档介绍unordered_set的构造接口使用 unordered_multisetOJ练习961.在长度2…

( 回溯算法) 27. 移除元素 ——【Leetcode每日一题】

❓27. 移除元素 难度&#xff1a;简单 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以…

ip地址段分解与合并

1、为什么要分解和合并ip地址段 无他&#xff0c;工作需要嘛&#xff0c;谁没事去划分ip地址段 优点&#xff1a;可以节省大量的时间&#xff0c;减少算错的可能性 2、工具下载 下载链接&#xff1a; https://github.com/zhanhb/cidr-merger github在国内使用不太友好&#…

14、IIC主机控制--引脚软件模拟

时序图&#xff1a; 软件基于STM32 HAL库 IIC–定时器精确延时 软件用涉及到使用定时器做精确延时&#xff0c;可以参考我的文章–“CubeMx 定时器高精度延时” 延时使用的文件&#xff1a; tim.c /*********************************************************************…

Linux基础内容(21)—— 进程消息队列和信号量

Linux基础内容&#xff08;20&#xff09;—— 共享内存_哈里沃克的博客-CSDN博客 目录 1.消息队列 1.定义 2.操作 2.信号量 1.定义 2.细节 3.延申 4.操作 3.IPC的特点共性 1.消息队列 1.定义 定义&#xff1a;是操作系统提供的内核级队列 2.操作 msgget&#xff1a;…

Java实现MQTT传输协议通信

Java实现MQTT传输协议通信 1. MQTT1.1 概述1.2 发布和订阅模型1.3 客户端1.4 服务器1.5 订阅、主题、会话1.6 协议中的方法2. Java使用MQTT2.1 添加 pom 依赖2.3 订阅方2.4 发布方2.4 MQTT 连接创建方式2.4.1 普通 TCP 连接2.4.2 TLS/SSL 连接1. MQTT

java_day01_单元测试_配置文件

一、软件的生命周期 **软件的可行性分析:**分析该软件是否值的研发,会消耗多少成本,能带来多少的利益等分析 **需求分析:**分析该软件具体该具备有那些功能,产品经理与客户一起讨论 **软件设计:**该软件应该使用什么样的架构,用什么样的数据库,每个模块的具体功能 **程序编…

2023年8大黑客编程语言

以下是2023年最适合黑客攻击的8种编程语言的列表。 道德黑客被定义为合法进入各种网络的做法&#xff0c;目的是识别黑客可能利用的潜在弱点来访问网络。此类黑客攻击旨在在任何漏洞进入危险攻击者手中之前发现它们&#xff0c;然后及时修复它们以防止攻击。让我们进入文章&am…