系统性学习数据结构-第三讲-栈和队列

  • 1. 栈
    • 1.1 栈和队列
    • 1.2 栈的实现
  • 2. 队列
    • 2.1 概念与结构
    • 2.2 队列的实现
  • 3. 栈和队列算法题
    • 3.1 [有效的括号](https://leetcode.cn/problems/valid-parentheses/description/)
    • 3.2 [用队列实现栈](https://leetcode.cn/problems/implement-stack-using-queues/description/)
    • 3.3 [用栈实现队列](https://leetcode.cn/problems/implement-queue-using-stacks/description/)

1. 栈

1.1 栈和队列

:⼀种特殊的线性表,其只允许在固定的⼀端进行插入和删除元素操作。进行数据插入和删除操作的⼀端称为栈顶,另⼀端称为栈底。

栈中的数据元素遵守后进先出 LIFO(Last In First Out)的原则。

压栈:栈的插入操作叫做进栈/压栈/入栈,入数据在栈顶。

出栈:栈的删除操作叫做出栈。出数据也在栈顶。

在这里插入图片描述

栈底层结构选型

栈的实现⼀般可以使用数组或者链表实现,相对而言数组的结构实现更优⼀些。因为数组在尾上插入数据的代价比较小。

1.2 栈的实现

stack. h

typedef int STDataType;
typedef struct Stack
{STDataType* a;int top;int capacity;
}ST;
// 初始化栈
void STInit(ST* ps);
// 销毁栈
void STDestroy(ST* ps);
// ⼊栈
void STPush(ST* ps, STDataType x);
//出栈
void STPop(ST* ps);
//取栈顶元素
STDataType STTop(ST* ps);
//获取栈中有效元素个数
int STSize(ST* ps);
//栈是否为空
bool STEmpty(ST* ps);

Stack.c

#include "Stack.h"void STInit(ST* ps)
{ps->arr = NULL;ps->capacity = ps->top = 0;
}void STDestroy(ST* ps)
{if(ps->arr)free(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}void StackPush(ST* ps, STDataType x)
{assert(ps);if (ps->capacity == ps->top){int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* newStack = (STDataType*)realloc(ps->arr, sizeof(STDataType) * NewCapacity);if (newStack == NULL){perror("malloc fail:");exit(1);}ps->arr = newStack;ps->capacity = NewCapacity;}ps->arr[ps->top++] = x;
}void StackPop(ST* ps)
{assert(ps);ps->top--;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}STDataType StackTop(ST* ps)
{assert(ps);return ps->arr[ps->top - 1];
}int STSize(ST* ps)
{assert(ps);return ps->top;
}

2. 队列

2.1 概念与结构

概念:只允许在⼀端进行插入数据操作,在另⼀端进行删除数据操作的特殊线性表,队列具有先进先出 FIFO (First In First Out)

⼊队列:进行插入操作的⼀端称为队尾

出队列:进行删除操作的一端成为队头

在这里插入图片描述

队列底层结构选型

队列也可以数组和链表的结构实现,使用链表的结构实现更优⼀些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

2.2 队列的实现

Queue.h

typedef int QDataType;
//队列结点结构
typedef struct QueueNode
{int val;struct QueueNode* next;
}QNode;
//队列结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;
//初始化队列
void QueueInit(Queue* pq);
//销毁队列
void QueueDestroy(Queue* pq);
// ⼊队列,队尾
void QueuePush(Queue *pq, QDataType x);
// 出队列,队头
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//队列有效元素个数
int QueueSize(Queue* pq);

Queue.c

#include"Queue.h"void QueueInit(Queue* pq)
{pq->head = pq->list = NULL;pq->size = 0;
}void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->head;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->list = NULL;
}void QueuePush(Queue* pq, QDataTpe x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail:");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->head == NULL){pq->head = pq->list = newnode;pq->size++;}else{pq->list->next = newnode;pq->size++;pq->list = pq->list->next;}
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}void QueuePop(Queue* pq)
{assert(!(QueueEmpty(pq)));if (pq->list == pq->head){free(pq->head);pq->head = pq->list = NULL;}else{QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}QDataTpe QueueFront(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->head->data;
}QDataTpe QueueBack(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->list->data;
}int QueueSize(Queue* pq)
{return pq->size;
}

3. 栈和队列算法题

3.1 有效的括号

typedef char StackDateTpye;
typedef struct Stack
{StackDateTpye* arr;int top;            //指向栈顶的位置int capacity;       //容量
}Stack;
//栈的初始化
void StackInit(Stack* st)
{assert(st);st->arr = NULL;st->capacity = 0;st->top = 0;
}//入栈-栈顶
void StackPush(Stack* st,StackDateTpye data)
{assert(st);if (st->top == st->capacity){int NewCapacity = st->capacity == 0 ? 4 : 2 * st->capacity;StackDateTpye* New = (StackDateTpye*)realloc(st->arr, NewCapacity*sizeof(Stack));if (New == NULL){perror("realloc fail:");exit(1);}st->arr = New;    //将数组换到新地址st->capacity = NewCapacity;  //将容量更新}st->arr[st->top++] = data;  //将栈顶位置更新
}
//判断栈是否为空
bool StackEmpty(Stack* st)
{assert(st);return st->top == 0;
}//出栈
void StackPop(Stack* st)
{assert(!StackEmpty(st));st->top--;
}//取栈顶数据
StackDateTpye StackTopData(Stack* st)
{assert(!StackEmpty(st));return st->arr[st->top-1];
}void STDestroy(Stack* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}
//获取栈中有效元素个数
int StackSize(Stack* st)
{assert(st);return st->top;
}
bool isValid(char* s) {Stack* st=(Stack*)malloc(sizeof(Stack));StackInit(st);while(*s!='\0'){if(*s=='('||*s=='['||*s=='{'){StackPush(st,*s);}    else{   if(StackEmpty(st)){STDestroy(st);return false;}if((*s==')'&&StackTopData(st)!='(')||(*s==']'&&StackTopData(st)!='[')||(*s=='}'&&StackTopData(st)!='{')){STDestroy(st);return false;}elseStackPop(st);}s++;}bool ret=StackEmpty(st)?true:false;STDestroy(st);return ret;}

在解答这道题时,我们使用上我们刚刚实现的栈结构,遇见左括号就入栈,遇见右括号就取栈顶与之配对,如果是对应的括号,

那就进行出栈操作,最后对栈进行判空,若为空则为有效的括号。

3.2 用队列实现栈

typedef int QDataTpe;
typedef struct QueueNode  //队列节点的结构
{QDataTpe data;struct QueueNode* next;
}QueueNode;typedef struct Queue  //队列的结构
{QueueNode* head; //队头QueueNode* list; //队尾int size;    //有效数据个数
}Queue;bool QueueEmpty(Queue* pq)
{assert(pq);return pq->size == 0;
}void QueueInit(Queue* pq)
{pq->head = pq->list = NULL;pq->size = 0;
}void QueuePush(Queue* pq, QDataTpe x)
{assert(pq);QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail:");exit(1);}newnode->data = x;newnode->next = NULL;if (pq->head == NULL){pq->head = pq->list = newnode;pq->size++;}else{pq->list->next = newnode;pq->size++;pq->list = pq->list->next;}
}int QueueSize(Queue* pq)
{return pq->size;
}QDataTpe QueueFront(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->head->data;
}void QueuePop(Queue* pq)
{assert(!(QueueEmpty(pq)));if (pq->list == pq->head){free(pq->head);pq->head = pq->list = NULL;}else{QueueNode* next = pq->head->next;free(pq->head);pq->head = next;}pq->size--;
}QDataTpe QueueBack(Queue* pq)
{assert(!(QueueEmpty(pq)));return pq->list->data;
}void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->head;while (pcur){QueueNode* next = pcur->next;free(pcur);pcur = next;}pq->head = pq->list = NULL;
}typedef struct {Queue PushQueue;Queue PopQueue;
} MyStack;MyStack* myStackCreate() {MyStack* mystack = (MyStack*)malloc(sizeof(MyStack));QueueInit(&mystack->PushQueue);QueueInit(&mystack->PopQueue);return mystack;
}void myStackPush(MyStack* obj, int x) {QueuePush(&obj->PushQueue, x);
}int myStackPop(MyStack* obj) {while(QueueSize(&obj->PushQueue) != 1){int data = QueueFront(&obj->PushQueue);QueuePop(&obj->PushQueue);QueuePush(&obj->PopQueue, data);}int data = QueueFront(&obj->PushQueue);QueuePop(&obj->PushQueue);while(QueueSize(&obj->PopQueue) != 0){int data = QueueFront(&obj->PopQueue);QueuePop(&obj->PopQueue);QueuePush(&obj->PushQueue, data);}return data;
}int myStackTop(MyStack* obj) {return QueueBack(&obj->PushQueue);
}bool myStackEmpty(MyStack* obj) {if(QueueEmpty(&obj->PushQueue) && QueueEmpty(&obj->PopQueue))return true;elsereturn false;
}void myStackFree(MyStack* obj) {QueueDestroy(&obj->PushQueue);QueueDestroy(&obj->PopQueue);obj = NULL;
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/

在这道题中,我们创建两个队列,栈遵循先进后出的规律,所以仅仅一个队列是无法完成要求的,定义一个栈为 PushQueue

对于入栈的数据我们直接入到这个队列中,定义一个栈为 PopQueue ,对于出栈操作,我们就将 PushQueue 中除了队头的数据,

全部迁移到 PopQueue 中,然后对PushQueue 进行出队操作, 最后再将 PopQueue 中的数据再迁移回来即可,若要取栈顶元素,

我们就重复上述步骤,但不进行出队操作即可。

3.3 用栈实现队列

typedef int STDataType;
typedef struct Stack
{STDataType* arr;int top;         //指向栈顶的位置int capacity;     //容量
}ST;void STInit(ST* ps)
{ps->arr = NULL;ps->capacity = ps->top = 0;
}void STDestroy(ST* ps)
{if(ps->arr)free(ps);ps->arr = NULL;ps->capacity = ps->top = 0;
}void StackPush(ST* ps, STDataType x)
{assert(ps);if (ps->capacity == ps->top){int NewCapacity = ps->capacity == 0 ? 4 : ps->capacity * 2;STDataType* newStack = (STDataType*)realloc(ps->arr, sizeof(STDataType) * NewCapacity);if (newStack == NULL){perror("malloc fail:");exit(1);}ps->arr = newStack;ps->capacity = NewCapacity;}ps->arr[ps->top++] = x;
}void StackPop(ST* ps)
{assert(ps);ps->top--;
}bool StackEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}STDataType StackTop(ST* ps)
{assert(ps);return ps->arr[ps->top - 1];
}int STSize(ST* ps)
{assert(ps);return ps->top;
}typedef struct {ST PushStack;ST PopStack;
} MyQueue;MyQueue* myQueueCreate() {MyQueue* myQueue = (MyQueue*)malloc(sizeof(MyQueue));STInit(&myQueue->PushStack);STInit(&myQueue->PopStack);return myQueue;
}void myQueuePush(MyQueue* obj, int x) {StackPush(&obj->PushStack, x);
}int myQueuePop(MyQueue* obj) {while(STSize(&obj->PushStack) != 1){int top = StackTop(&obj->PushStack);StackPush(&obj->PopStack, top);StackPop(&obj->PushStack);}int final = StackTop(&obj->PushStack);StackPop(&obj->PushStack);while(STSize(&obj->PopStack) != 0){int top = StackTop(&obj->PopStack);StackPush(&obj->PushStack, top);StackPop(&obj->PopStack);}return final;
}int myQueuePeek(MyQueue* obj) {while(STSize(&obj->PushStack) != 1){int top = StackTop(&obj->PushStack);StackPush(&obj->PopStack, top);StackPop(&obj->PushStack);}int final = StackTop(&obj->PushStack);while(STSize(&obj->PopStack) != 0){int top = StackTop(&obj->PopStack);StackPush(&obj->PushStack, top);StackPop(&obj->PopStack);}return final;
}bool myQueueEmpty(MyQueue* obj) {if(StackEmpty(&obj->PopStack) && StackEmpty(&obj->PushStack))return true;elsereturn false;
}void myQueueFree(MyQueue* obj) {free(obj);obj = NULL;
}/*** Your MyQueue struct will be instantiated and called as such:* MyQueue* obj = myQueueCreate();* myQueuePush(obj, x);* int param_2 = myQueuePop(obj);* int param_3 = myQueuePeek(obj);* bool param_4 = myQueueEmpty(obj);* myQueueFree(obj);
*/

对于用栈实现队列,我们采用的思路与用队列实现栈并无太大差别,阅读代码即可,在这里不再赘述。

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

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

硬件(三) 通信方式、串口通信

一、通信类型(一)并行通信多个比特通过并行线同时传输,传输速率快,但会大量占用芯片资源,在对资源敏感的场景下不太适用。(二)串行通信把数据拆成单个比特,按顺序在一根总线上发送。…

vsan default storage policy 具体是什么策略?

vSAN Default Storage Policy(vSAN 默认存储策略)是 VMware vSAN 部署后自动创建的基础存储策略,其核心目标是在“通用性”和“可靠性”之间取得平衡,为大多数虚拟机提供默认的数据保护和存储服务,无需管理员手动创建策…

雨后阳光为何更强烈?

1. 降雨后的辐射是否会增强一般来说,降雨时天空多云,云层对太阳辐射有强烈削弱作用,所以降雨时的短波辐射显著下降。但雨后,空气湿度大、颗粒物被冲刷、天空转晴时,大气透明度会提高,短波辐射相较于降雨前往…

美团发布 | LongCat-Flash最全解读,硬刚GPT-4.1、Kimi!

一、导读 本报告解析了美团LongCat团队推出的LongCat-Flash模型,一个拥有5600亿参数的混合专家模型(Mixture-of-Experts, MoE)。面对大规模语言模型在计算资源和效率上的挑战,LongCat-Flash旨在实现计算效率与高级智能体&#xf…

Ubuntu 18.04 上升级 gcc 到 9.4

18.04 默认的源中可能没有 GCC-9.3 或更新版本,在终端运行以下命令来添加 PPA: sudo add-apt-repository ppa:ubuntu-toolchain-r/test sudo apt update2.安装 GCC 和 G sudo apt install gcc-9 g-93.更新替代版本 如果系统中安装了多个 GCC 版本&#x…

.NET GcPDF V8.2 新版本:人工智能 PDF 处理

一、GcPDF 产品简介 GcPDF(GrapeCity Documents for PDF)是葡萄城(GrapeCity)推出的一款功能强大的 .NET PDF 开发组件,旨在为开发人员提供高效、灵活的 PDF 文档处理解决方案。无论是创建全新 PDF 文档、编辑现有 PD…

解锁桐果云零代码数据平台能力矩阵——赋能零售行业数字化转型新动能

在零售行业从“规模扩张”转向“精细运营”的当下,数据已成为优化库存、精准营销、防控风险的核心抓手。但多数零售企业仍面临“数据杂乱难治理、分析建模门槛高、场景适配性不足”等难题,导致大量订单、商品、交易数据沉睡,难以转化为经营决…

rabbitmq 入门知识点

RabbitMQ 是一个 消息队列中间件(Message Broker),实现了 AMQP 协议,常用于服务之间解耦、异步处理、流量削峰等场景。 我帮你分成两个部分来讲:核心原理 常见用法。🧩 一、核心原理 RabbitMQ 的核心是 生…

点控云智能客服:以AI重塑服务体验,登顶行业第一的革新之路

在数字化浪潮席卷全球的今天,客户服务已成为企业核心竞争力之一。智能客服作为连接企业与客户的重要桥梁,其效能与体验直接关系到企业的品牌形象与市场口碑。近日,权威机构发布的《中国智能客服市场竞争力报告》显示,点控云智能客…

9.5 IO-线程day5

信号量打印ABC#include <stdio.h> #include <string.h> #include <stdlib.h> #include <25061head.h> sem_t sem[1]; void *callback(void *arg) {while(1){sem_wait(&sem[0]);printf("A\n");sleep(1);sem_post(&sem[1]);}pthread_e…

老师如何高效收集学生学籍信息,完成收集工作?

开学的时光总是忙碌而充实&#xff0c;除了要热情地迎接新生、用心地备课&#xff0c;还有一件让人头疼不已的事情——学生学籍信息的收集。上学期开学&#xff0c;我承担起了收集班级新生信息的重任&#xff0c;满心以为提前准备好的纸质表格&#xff0c;在新生报到那天发给家…

JAVA层的权限与SELinux的关系

Java 层权限是应用程序级别的“门禁卡”&#xff0c;而 SELinux 是系统级别的“防火墙规则和强制访问控制”。即使你拥有进入大楼的“门禁卡”&#xff08;Java 权限&#xff09;&#xff0c;如果“防火墙规则”&#xff08;SELinux 策略&#xff09;不允许你的进程与目标服务或…

Screen 三步上手

好的&#xff0c;这是给同事的简洁版说明&#xff1a;Screen 三步上手 开新窗口&#xff1a;干活前先开个带名字的窗口&#xff0c;不怕断连。 screen -S 任务名看所有窗口&#xff1a;随时查看都有哪些任务在后台跑。 screen -ls重回窗口&#xff1a;断连后重新登录&#xff0…

flink 伪代码

import java.util.*; import java.util.concurrent.*;// 核心接口定义 interface StreamOperator {void open();void processElement(Object element);void close(); }interface SourceFunction extends StreamOperator {void run(SourceContext ctx); }interface SinkFunction…

一招快速识别你的电脑是机械硬盘还是固态硬盘

你是否经常觉得电脑开机慢、软件打开卡顿&#xff1f;其中一个关键原因&#xff0c;可能就在于你使用的是机械硬盘&#xff08;HDD&#xff09;还是固态硬盘&#xff08;SSD&#xff09;。固态硬盘读写速度快&#xff0c;能显著提升系统响应速度&#xff1b;而机械硬盘虽然容量…

52核心52线程,Intel下一代CPU憋了个大的

被逼急了的 Intel&#xff0c;可能正在憋大招&#xff01;如大伙儿所见&#xff0c;Intel 这两年日子已经不能用「惨」来形容。其过去引以为傲的 PC 处理器&#xff0c;特别是高性能桌面处理器领域&#xff0c;如今算是彻底被 AMD 打懵了。无他&#xff0c;己方产品是连年摆烂&…

【LeetCode 热题 100】1. 两数之和——(解法二)哈希表

Problem: 1. 两数之和 文章目录整体思路完整代码时空复杂度时间复杂度&#xff1a;O(N)空间复杂度&#xff1a;O(N)整体思路 这段代码旨在高效地解决 “两数之和” 问题。与 O(N^2) 的暴力枚举法相比&#xff0c;此版本采用了一种经典的 “空间换时间” 策略&#xff0c;利用 …

MySQL主从同步--主从复制进阶

MySQL支持一台主库同时向多台从库进行复制&#xff0c;从库同时也可以作为其他从服务器的主库&#xff0c;实现链状复制。1、MySQL支持的binlog二进制日志复制类型- 基于语句&#xff08;statement&#xff09;的复制在主服务器上执行SQL语句&#xff0c;在从服务器上执行同样的…

WPF外部打开html文件

注意&#xff1a;这是一份提供WPF外部浏览器打开html的方法&#xff0c;而不是WPF内部嵌入html 需要通过浏览器打开&#xff0c;否则无法使用地址栏拼接参数的形式操作html 下面是打开html的方法↓string localHtmlPath "C:\Users\pangb\Downloads\Help\帮助文档 - 副本.…

Go初级之十:错误处理与程序健壮性

Go初级之十&#xff1a;错误处理与程序健壮性为什么选这个主题&#xff1f; 错误处理是 Go 语言中一个非常独特且重要的设计哲学。它体现了 Go 的“显式错误处理”思想&#xff0c;与其它语言&#xff08;如 Java/Python&#xff09;的异常机制不同。在实际开发中&#xff0c;几…