嵌入式培训之数据结构学习(五)栈与队列
一、栈
(一)栈的基本概念
1、栈的定义:
注:线性表中的栈在堆区(因为是malloc来的);系统中的栈区存储局部变量、函数形参、函数返回值地址。
2、栈顶和栈底:
允许插入和删除的一端称为栈顶(top),另一端称为栈底(bottom),不含任何数据元素的栈称为空栈。
3、 LIFO 结构:栈又称为后进先出(LastInFirst0ut)的线性表。
4、栈示意图:
top为栈顶指针
5、栈对线性表的插入和删除的位置进行了限制,并没有对元素进出的时间进行限制,
也就是说,在不是所有元素都进栈的情况下,事先进去的元素也可以出栈,只要保证是
栈顶元素出栈就可以。
6、应用:解决的问题要回溯、递归可以用链栈;
有优先级问题的用栈处理。
(1)直观应用(eg:gdb调试中的where命令):函数调用返回原函数用栈结构(一层
一层进、一层一层退);
(2)(3 + 5 * 6)四则运算表达式
优先级问题、字符串解析(先扫一遍表达式,再套用优先级顺序对优先级高的优先处理,最后在退回来处理表达式)。
(二)链栈的基本操作
1、创建链栈
LinkStack* CreateLinkStack()
{
// 分配链栈结构体空间,void* 强转为 LinkStack*
LinkStack* ls = (LinkStack*)malloc(sizeof(LinkStack));
if(NULL == ls)
{
// 打印错误信息到标准错误流
fprintf(stderr,"CreateLinkStack malloc\n");
return NULL; // 分配失败返回空
}
ls->top = NULL; // 初始化栈顶指针为空(空栈)
ls->clen = 0; // 初始化栈长度为0
return ls; // 返回创建好的链栈指针
}
2、入栈
int PushLinkStack(LinkStack* ls, DATATYPE* data)
{
// 分配新节点空间
LinkStackNode* newnode = malloc(sizeof(LinkStackNode));
if(NULL == newnode)
{
fprintf(stderr,"PushLinkStack malloc\n"); // 打印内存分配失败信息
return 1; // 失败返回错误码1
}
// 将数据拷贝到新节点的数据域
memcpy(&newnode->data, data, sizeof(DATATYPE));
newnode->next = NULL; // 新节点初始next指针为空
newnode->next = ls->top; // 新节点next指向原栈顶节点(头插法)
ls->top = newnode; // 栈顶更新为新节点
ls->clen++; // 栈长度加1
return 0; // 成功返回0(原代码缺失return,需补充)
}
3、出栈
int PopLinkStack(LinkStack* ls)
{
if(IsEmptyLinkStack(ls)) // 检查栈是否为空
{
return 1; // 空栈返回错误码1
}
LinkStackNode* tmp = ls->top; // 保存当前栈顶节点指针
ls->top = ls->top->next; // 栈顶指针后移一位(指向原次栈顶)
free(tmp); // 释放原栈顶节点内存
ls->clen--; // 栈长度减1
return 0; // 成功返回0
}
4、判断栈空
int IsEmptyLinkStack(LinkStack* ls)
{
// 栈长度为0时返回1(真),否则返回0(假)
return 0 == ls->clen;
}
5、获取栈顶元素
DATATYPE* GetTopLinkStack(LinkStack* ls)
{
if(IsEmptyLinkStack(ls)) // 检查栈是否为空
{
return NULL; // 空栈返回空指针
}
// 返回栈顶节点数据域的地址(需确保调用时不为空)
return &ls->top->data;
}
6、销毁链栈
int DestroyLinkStack(LinkStack* ls)
{
int len = GetSizeLinkStack(ls); // 获取栈当前长度
for(int i = 0; i < len; ++i)
{
PopLinkStack(ls); // 循环调用出栈函数释放所有节点
}
free(ls); // 释放链栈结构体本身的内存
return 0; // 成功返回0
}
注:查看是否内存泄漏:valgrind ./app
7、获取栈长度
int GetSizeLinkStack(LinkStack* ls)
{
// 直接返回结构体中记录的栈长度(O(1)时间复杂度)
return ls->clen;
}
(三)栈练习:C语言文本检查器(符号匹配)
1、代码:
(1)文件读取函数 readfile
int readfile(char *filename, char *filecontext)
{
// 以只读模式打开文件
FILE *fp = fopen(filename, "r");
if (NULL == fp)
{
return 1; // 打开失败返回1
}
// 从文件读取1024字节到filecontext缓冲区,返回实际读取块数(此处块大小为1字节)
fread(filecontext, 1024, 1, fp);
fclose(fp); // 关闭文件
return 0; // 成功返回0
}
(2) 主函数 main
int main(int argc, char **argv)
{
char buf[1024] = {0}; // 初始化1024字节缓冲区
int ret = readfile("/home/linux/2.c", buf); // 读取文件内容到buf
if (1 == ret)
{
return 1; // 读取失败直接退出
}LinkStack *ls = CreateLinkStack(); // 创建链栈用于符号匹配
char *tmp = buf; // 临时指针指向缓冲区起始位置
DATATYPE data = {0}; // 存储符号及其行列信息的结构体
int row = 1; // 当前行号,初始为1
int col = 1; // 当前列号,初始为1
DATATYPE *top; // 用于存储栈顶元素的指针while (*tmp) // 遍历缓冲区直到遇到'\0'
{
memset(&data, 0, sizeof(DATATYPE)); // 清空结构体数据
switch (*tmp) // 根据当前字符判断类型
{
// 处理左括号:( [ {
case '(':
case '[':
case '{':
data.c = *tmp; // 存储符号
data.col = col; // 存储列号
data.row = row; // 存储行号
PushLinkStack(ls, &data); // 将符号入栈
break;// 处理右括号:)
case ')':
top = GetTopLinkStack(ls); // 获取栈顶元素
// 栈顶存在且为匹配的'('
if ('(' == top->c && NULL != top)
{
PopLinkStack(ls); // 匹配成功,出栈
}
else
{
if (NULL == top) // 栈空,说明缺少左括号
{
// 输出错误:右括号无匹配左括号
printf("sym:%c row:%d col%d\n", ')', row, col);
}
else // 栈顶符号不匹配(如栈顶是'['或'{')
{
// 输出错误:栈顶符号与当前右括号不匹配
printf(
"top sym:%c row:%d col%d , or file sym:%c row:%d col%d\n",
top->c, top->row, top->col, *tmp, row, col);
}
return 1; // 匹配失败,程序退出
}
break;// 处理右括号:](逻辑同')')
case ']':
top = GetTopLinkStack(ls);
if ('[' == top->c && NULL != top)
{
PopLinkStack(ls);
}
else
{
if (NULL == top)
{
printf("sym:%c row:%d col%d\n", ']', row, col); // 注意此处误写为')',应为']'
}
else
{
printf(
"top sym:%c row:%d col%d , or file sym:%c row:%d col%d\n",
top->c, top->row, top->col, *tmp, row, col);
}
return 1;
}
break;// 处理右括号:}(逻辑同')')
case '}':
top = GetTopLinkStack(ls);
if ('{' == top->c && NULL != top)
{
PopLinkStack(ls);
}
else
{
if (NULL == top)
{
printf("sym:%c row:%d col%d\n", '}', row, col); // 注意此处误写为')',应为'}'
}
else
{
printf(
"top sym:%c row:%d col%d , or file sym:%c row:%d col%d\n",
top->c, top->row, top->col, *tmp, row, col);
}
return 1;
}
break;
}col++; // 列号递增
if ('\n' == *tmp) // 遇到换行符
{
col = 1; // 列号重置为1
row++; // 行号递增
}
tmp++; // 移动到下一个字符
}// 遍历结束后检查栈是否为空(所有左括号是否匹配)
if ('\0' == *tmp && IsEmptyLinkStack(ls))
{
printf("ok"); // 完全匹配,输出"ok"
}
else
{
top = GetTopLinkStack(ls); // 获取剩余未匹配的左括号
// 输出未匹配的左括号及其位置
printf("top sym:%c row:%d col%d\n", top->c, top->row, top->col);
}DestroyLinkStack(ls); // 销毁链栈,释放内存
return 0;
}
2、输出结果:匹配成功输出ok,匹配不成功输出符号及其位置。
二、队列
(一)队列的基本概念
1、队列的定义:
2、FIFO结构:队列是一种先进先出(FirstInFirst0ut)的线性表。
允许插入的一 端称为队尾,允许删除的一端称为队头。
3、队列示意图
clen = (尾巴 - 头 + 长度)
4、应用:计算机做缓冲用队列;
5、循环队列(圆环):(求余就循环起来)
6、面试问题:满队判断条件:尾巴+1==头 (tail + 1 ) % tlen == head
空队:尾巴==头 tail == head
(二)队列的基本操作
1、创建顺序队列
SeqQueue* CreateSeqQue(int len)
{
// 分配顺序队列结构体空间,强转为 SeqQueue*
SeqQueue* sq = (SeqQueue*)malloc(sizeof(SeqQueue));
if(NULL == sq)
{
// 打印结构体内存分配失败信息到标准错误流
fprintf(stderr,"CreateSeqQue malloc\n");
return NULL; // 失败返回空
}
// 分配队列数据存储数组空间(容量为len)
sq->array = malloc(sizeof(DATATYPE)*len);
if(NULL == sq->array)
{
// 打印数组内存分配失败信息
fprintf(stderr,"CreateSeqQue malloc\n");
free(sq); // 释放已分配的结构体空间(避免内存泄漏)
return NULL;
}
sq->head = 0; // 初始化头指针(指向队头元素前一个位置)
sq->tail = 0; // 初始化尾指针(指向队尾元素位置)
sq->tlen = len; // 记录队列总长度(容量)
return sq; // 返回创建好的顺序队列指针
}
2、判断队空
int IsEmptySeqQue(SeqQueue* sq)
{
// 头指针等于尾指针时队列为空(返回1表示真,0表示假)
return sq->head == sq->tail;
}
3、判断队满
int IsFullSeqQue(SeqQueue* sq)
{
// 尾指针下一个位置(循环取模)等于头指针时队满
return (sq->tail + 1) % sq->tlen == sq->head;
}
4、入队
int EnterSeqQue(SeqQueue* sq, DATATYPE* data)
{
if(IsFullSeqQue(sq))
{
// 打印队满错误信息
fprintf(stderr,"EnterSeqQue,SeqQueue full\n");
return 1; // 失败返回错误码1
}
// 将数据拷贝到尾指针当前位置的数组元素中
memcpy(&sq->array[sq->tail], data, sizeof(DATATYPE));
// 尾指针后移一位(循环队列,取模实现环形)
sq->tail = (sq->tail + 1) % sq->tlen;
return 0; // 成功返回0
}
5、出队
int QuitSeqQue(SeqQueue* sq)
{
if(IsEmptySeqQue(sq))
{
// 打印队空错误信息
fprintf(stderr,"QuitSeqQue SeqQueue empty\n");
return 1; // 失败返回错误码1
}
// 头指针后移一位(指向实际队头元素,取模实现环形)
sq->head = (sq->head + 1) % sq->tlen;
return 0; // 成功返回0
}
6、获取队头元素
DATATYPE* GetHeadSeqQue(SeqQueue* sq)
{
if(IsEmptySeqQue(sq))
{
return NULL; // 队空返回空指针
}
// 返回队头元素地址(头指针当前指向的是队头元素的位置)
return &sq->array[sq->head];
}
7、销毁顺序队列
int DestroySeqQue(SeqQueue* sq)
{
free(sq->array); // 先释放数据存储数组的内存
free(sq); // 再释放队列结构体的内存
return 0; // 成功返回0
}
相关文章:

嵌入式培训之数据结构学习(五)栈与队列
一、栈 (一)栈的基本概念 1、栈的定义: 注:线性表中的栈在堆区(因为是malloc来的);系统中的栈区存储局部变量、函数形参、函数返回值地址。 2、栈顶和栈底: 允许插入和删除的一端…...

RabbitMQ--进阶篇
RabbitMQ 客户端整合Spring Boot 添加相关的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-amqp</artifactId> </dependency> 编写配置文件,配置RabbitMQ的服务信息 spri…...

Android Studio报错Cannot parse result path string:
前言 最近在写个小Demo,参考郭霖的《第一行代码》,学习DrawerLayout和NavigationView,不知咋地,突然报错Cannot parse result path string:xxxxxxxxxxxxx 反正百度,问ai都找不到答案,报错信息是完全看不懂…...
matlab求矩阵的逆、行列式、秩、转置
inv - 计算矩阵的逆 用途:计算一个可逆矩阵的逆矩阵。 D [1, 2; 3, 4]; % 定义一个2x2矩阵 D_inv inv(D); % 计算矩阵D的逆 disp(D_inv);det - 计算矩阵的行列式 用途:计算方阵的行列式。 E [1, 2; 3, 4]; determinant det(E); % 计算行列式 disp…...

关于网站提交搜索引擎
发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由: 提高可见性:通过将您的网站提交给搜索引擎,可以提高您的网站在搜索结果中出现的机会。当用…...
计算机视觉与深度学习 | Python实现EMD-SSA-VMD-LSTM-Attention时间序列预测(完整源码和数据)
EMD-SSA-VMD-LSTM-Attention 一、完整代码实现二、代码结构解析三、关键数学公式四、参数调优建议五、性能优化方向六、工业部署建议 以下是用Python实现EMD-SSA-VMD-LSTM-Attention时间序列预测的完整解决方案。该方案结合了四层信号分解技术与注意力增强的深度学习模型&#…...
二进制与十进制互转的方法
附言: 在计算机科学和数字系统中,二进制和十进制是最常见的两种数制。二进制是计算机内部数据存储和处理的基础,而十进制则是我们日常生活中最常用的数制。因此,掌握二进制与十进制之间的转换方法对于计算机学习者和相关领域的从业者来说至关…...
05、基础入门-SpringBoot-HelloWorld
05、基础入门-SpringBoot-HelloWorld ## 一、Spring Boot 简介 **Spring Boot** 是一个用于简化 **Spring** 应用初始搭建和开发的框架,旨在让开发者快速启动项目并减少配置文件。 ### 主要特点 - **简化配置**:采用“约定优于配置”的原则,减…...
LeetCode 153. 寻找旋转排序数组中的最小值:二分查找法详解及高频疑问解析
文章目录 问题描述算法思路:二分查找法关键步骤 代码实现代码解释高频疑问解答1. 为什么循环条件是 left < right 而不是 left < right?2. 为什么比较 nums[mid] > nums[right] 而不是 nums[left] < nums[mid]?3. 为什么 right …...

基于QT(C++)OOP 实现(界面)酒店预订与管理系统
酒店预订与管理系统 1 系统功能设计 酒店预订是旅游出行的重要环节,而酒店预订与管理系统中的管理与信息透明是酒店预订业务的关键问题所在,能够方便地查询酒店信息进行付款退款以及用户之间的交流对于酒店预订行业提高服务质量具有重要的意义。 针对…...
人工智能100问☞第25问:什么是循环神经网络(RNN)?
目录 一、通俗解释 二、专业解析 三、权威参考 循环神经网络(RNN)是一种通过“记忆”序列中历史信息来处理时序数据的神经网络,可捕捉前后数据的关联性,擅长处理语言、语音等序列化任务。 一、通俗解释 想象你在和朋友聊天,每说一句话都会根据之前的对话内容调整语气…...

机械元件杂散光难以把控?OAS 软件案例深度解析
机械元件的杂散光分析 简介 在光学系统设计与工程实践中,机械元件的杂散光问题对系统性能有着不容忽视的影响。杂散光会降低光学系统的信噪比、图像对比度,甚至导致系统功能失效。因此,准确分析机械元件杂散光并采取有效抑制措施,…...

游戏引擎学习第289天:将视觉表现与实体类型解耦
回顾并为今天的工作设定基调 我们正在继续昨天对代码所做的改动。我们已经完成了“脑代码(brain code)”的概念,它本质上是一种为实体构建的自组织控制器结构。现在我们要做的是把旧的控制逻辑迁移到这个新的结构中,并进一步测试…...

【Linux网络】ARP协议
ARP协议 虽然我们在这里介绍 ARP 协议,但是需要强调,ARP 不是一个单纯的数据链路层的协议,而是一个介于数据链路层和网络层之间的协议。 ARP数据报的格式 字段长度(字节)说明硬件类型2网络类型(如以太网为…...

MUSE Pi Pro 开发板 Imagination GPU 利用 OpenCL 测试
视频讲解: MUSE Pi Pro 开发板 Imagination GPU 利用 OpenCL 测试 继续玩MUSE Pi Pro,今天看下比较关注的gpu这块,从opencl看起,安装clinfo指令 sudo apt install clinfo 可以看到这颗GPU是Imagination的 一般嵌入式中gpu都和hos…...

多线程与线程互斥
我们初步学习完线程之后,就要来试着写一写多线程了。在写之前,我们需要继续来学习一个线程接口——叫做线程分离。 默认情况下,新创建的线程是joinable的,线程退出后,需要对其进行pthread_join操作,否则无法…...
使用Spring Boot和Spring Security构建安全的RESTful API
使用Spring Boot和Spring Security构建安全的RESTful API 引言 在现代Web开发中,安全性是构建应用程序时不可忽视的重要方面。本文将介绍如何使用Spring Boot和Spring Security框架构建一个安全的RESTful API,并结合JWT(JSON Web Token&…...

游戏引擎学习第287天:加入brain逻辑
Blackboard:动态控制类似蛇的多节实体 我们目前正在处理一个关于实体系统如何以组合方式进行管理的问题。具体来说,是在游戏中实现多个实体可以共同或独立行动的机制。例如,我们的主角拥有两个实体组成部分,一个是身体࿰…...

continue通过我们的开源 IDE 扩展和模型、规则、提示、文档和其他构建块中心,创建、共享和使用自定义 AI 代码助手
一、软件介绍 文末提供程序和源码下载 Continue 使开发人员能够通过我们的开源 VS Code 和 JetBrains 扩展以及模型、规则、提示、文档和其他构建块的中心创建、共享和使用自定义 AI 代码助手。 二、功能 Chat 聊天 Chat makes it easy to ask for help from an LLM without…...

2025年EB SCI2区TOP,多策略改进黑翅鸢算法MBKA+空调系统RC参数辨识与负载聚合分析,深度解析+性能实测
目录 1.摘要2.黑翅鸢优化算法BKA原理3.改进策略4.结果展示5.参考文献6.代码获取7.读者交流 1.摘要 随着空调负载在电力系统中所占比例的不断上升,其作为需求响应资源的潜力日益凸显。然而,由于建筑环境和用户行为的变化,空调负载具有异质性和…...

.NET 中管理 Web API 文档的两种方式
前言 在 .NET 开发中管理 Web API 文档是确保 API 易用性、可维护性和一致性的关键。今天大姚给大家分享两种在 .NET 中管理 Web API 文档的方式,希望可以帮助到有需要的同学。 Swashbuckle Swashbuckle.AspNetCore 是一个流行的 .NET 库,它使得在 AS…...
常见三维引擎坐标轴 webgl threejs cesium blender unity ue 左手坐标系、右手坐标系、坐标轴方向
平台 / 引擎坐标系类型Up(上)方向Forward(前进)方向前进方向依据说明Unity左手坐标系YZtransform.forward 是 Z 轴正方向,默认摄像机朝 Z 看。Unreal Engine左手坐标系ZXUE 的角色面朝 X,默认使用 GetActor…...

【HTML】个人博客页面
目录 页面视图编辑 页面代码 解释: HTML (<body>): 使用了更加语义化的HTML5标签,例如<header>, <main>, <article>, <footer>。文章列表使用了<article>包裹,结构清晰。添加了分页导航。使用了Font…...

论文解读:ICLR2025 | D-FINE
[2410.13842] D-FINE: Redefine Regression Task in DETRs as Fine-grained Distribution Refinement D-FINE 是一款功能强大的实时物体检测器,它将 DETRs 中的边界框回归任务重新定义为细粒度分布细化(FDR),并引入了全局最优定位…...

9.DMA
目录 DMA —为 CPU 减负 DMA 的简介和使用场景 DMA 的例子讲解 STM32 的 DMA 框图和主要特性 编辑 DMA 的通道的对应通道外设 – DMA 和哪些外设使用 编辑编辑ADC_DR 寄存器地址的计算 常见的数据滤波方法 ADCDMA 的编程 DMA —为 CPU 减负 DMA 的简介和使用场…...

大语言模型 10 - 从0开始训练GPT 0.25B参数量 补充知识之模型架构 MoE、ReLU、FFN、MixFFN
写在前面 GPT(Generative Pre-trained Transformer)是目前最广泛应用的大语言模型架构之一,其强大的自然语言理解与生成能力背后,是一个庞大而精细的训练流程。本文将从宏观到微观,系统讲解GPT的训练过程,…...

python基础语法(三-中)
基础语法3: 2.列表与元组: <1>.列表、元组是什么? 都用来存储数据,但是两者有区别,列表可变,元组不可变。 <2>.创建列表: 创建列表有两种方式: [1].a 【】&#x…...
【gitee 初学者矿建仓库】
简易的命令行入门教程: Git 全局设置: git config --global user.name "你的名字"触摸 git config --global user.email "你的邮箱"创建 git 仓库: mkdir codestore cd codestore git init -b "main" touch README.md # 选择运行 git add REA…...
思路收集文档
降低工作量思路 nodejsjava混合网站开发...
OpenCV 光流估计:从原理到实战
在计算机视觉领域,光流估计(Optical Flow Estimation)是一项至关重要的技术,它能够通过分析视频序列中图像像素的运动信息,捕捉物体和相机的运动情况。OpenCV 作为强大的计算机视觉库,为我们提供了高效实现…...