嵌入式培训之数据结构学习(五)栈与队列

一、栈

(一)栈的基本概念

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 
}

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

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

相关文章

深度学习---知识蒸馏(Knowledge Distillation, KD)

一、知识蒸馏的本质与起源 定义&#xff1a; 知识蒸馏是一种模型压缩与迁移技术&#xff0c;通过将复杂高性能的教师模型&#xff08;Teacher Model&#xff09;所学的“知识”迁移到轻量级的学生模型&#xff08;Student Model&#xff09;&#xff0c;使学生模型在参数量和计…

ARP Detection MAC-Address Static

一、ARP Detection&#xff08;ARP检测&#xff09; ✅ 定义&#xff1a; ARP检测是一种防止ARP欺骗攻击的安全机制。它通过监控或验证网络中的ARP报文&#xff0c;来判断是否存在伪造的ARP信息。 &#x1f50d; 工作原理&#xff1a; 网络设备&#xff08;如交换机&#xf…

基于 Python 的界面程序复现:标准干涉槽型设计计算及仿真

基于 Python 的界面程序复现&#xff1a;标准干涉槽型设计计算及仿真 在工业设计与制造领域&#xff0c;刀具的设计与优化是提高生产效率和产品质量的关键环节之一。本文将介绍如何使用 Python 复现一个用于标准干涉槽型设计计算及仿真的界面程序&#xff0c;旨在帮助工程师和…

Python绘制南丁格尔玫瑰图:从入门到实战

Python绘制南丁格尔玫瑰图&#xff1a;从入门到实战 引言 南丁格尔玫瑰图&#xff08;Nightingale Rose Chart&#xff09;&#xff0c;也被称为极区图&#xff08;Polar Area Chart&#xff09;&#xff0c;是一种独特的数据可视化方式。这种图表由弗洛伦斯南丁格尔&#xff…

计算机操作系统概要

不谋万世者&#xff0c;不⾜谋⼀时。不谋全局者 &#xff0c;足谋⼀域 。 ——陈澹然《寤⾔》《迁都建藩议》 操作系统 一.对文件简单操作的常用基础指令 ls ls 选项 目录或⽂件名:罗列当前⽬录下的⽂件 -l&#xff1a;以长格式显示⽂件和⽬录的详细信息 -a 或 --all&…

<PLC><视觉><机器人>基于海康威视视觉检测和UR机械臂,如何实现N点标定?

前言 本系列是关于PLC相关的博文,包括PLC编程、PLC与上位机通讯、PLC与下位驱动、仪器仪表等通讯、PLC指令解析等相关内容。 PLC品牌包括但不限于西门子、三菱等国外品牌,汇川、信捷等国内品牌。 除了PLC为主要内容外,相关设备如触摸屏(HMI)、交换机等工控产品,如果有…

从专家编码到神经网络学习:DTM 的符号操作新范式

1st author: Paul Soulos paper: Differentiable Tree Operations Promote Compositional Generalization ICML 2023 code: psoulos/dtm: Differentiable Tree Machine 1. 问题与思路 现代深度学习在连续向量空间中取得了巨大成功&#xff0c;然而在处理具有显式结构&#x…

微信小程序第三方代开发模式技术调研与实践总结

🚀 微信小程序第三方代开发模式技术调研与实践总结 📖 前言 随着企业对私有化品牌运营诉求的增加,许多大型客户希望将原本由 SaaS 平台统一提供的小程序迁移至自有主体(AppID)下运行,同时又希望继续沿用 SaaS 平台的业务服务与数据托管方式。微信开放平台提供的“小程…

开启智能未来:DeepSeek赋能行业变革之路

前言 在人工智能重构生产关系的2025年&#xff0c;DeepSeek以其革命性的推理能力和Python生态的技术延展性&#xff0c;正在重塑内容创作与数据智能的边界。本书以"工具迭代思维升维"为双轮驱动&#xff0c;构建从认知突破到商业落地的完整知识图谱。 DeepSeek的崛…

常见三维引擎坐标轴 webgl threejs cesium blender unity ue 左手坐标系、右手坐标系、坐标轴方向

平台 / 引擎坐标系类型Up&#xff08;上&#xff09;方向Forward&#xff08;前进&#xff09;方向前进方向依据说明Unity左手坐标系YZtransform.forward 是 Z 轴正方向&#xff0c;默认摄像机朝 Z 看。Unreal Engine左手坐标系ZXUE 的角色面朝 X&#xff0c;默认使用 GetActor…

Cold Diffusion: Inverting Arbitrary Image Transforms Without Noise论文阅读

冷扩散&#xff1a;无需噪声的任意图像变换反转 摘要 标准扩散模型通常涉及两个核心步骤&#xff1a;图像降质 &#xff08;添加高斯噪声&#xff09;和图像恢复 &#xff08;去噪操作&#xff09;。本文发现&#xff0c;扩散模型的生成能力并不强烈依赖于噪声的选择&#xf…

Java并发编程核心组件简单了解

一、Lock体系 1. ReentrantLock&#xff08;可重入锁&#xff09; Lock lock new ReentrantLock(); lock.lock(); try {// 临界区代码 } finally {lock.unlock(); }特点&#xff1a;可重入、支持公平/非公平策略优势&#xff1a;可中断锁获取、定时锁等待使用场景&#xff1…

第二个五年计划!

下一阶段&#xff01;5年后&#xff01;33岁&#xff01;体重维持在125斤内&#xff01;腰围74&#xff01; 健康目标&#xff1a; 体检指标正常&#xff0c;结节保持较小甚至变小&#xff01; 工作目标&#xff1a; 每年至少在一次考评里拿A&#xff08;最高S&#xff0c;A我理…

Redis(三) - 使用Java操作Redis详解

文章目录 前言一、创建项目二、导入依赖三、键操作四、字符串操作五、列表操作六、集合操作七、哈希表操作八、有序集合操作九、完整代码1. 完整代码2. 项目下载 前言 本文主要介绍如何使用 Java 操作 Redis 数据库&#xff0c;涵盖项目创建、依赖导入及 Redis 各数据类型&…

【Folium】使用离线地图

文章目录 相关文献离线地图下载Folium 使用离线地图 相关文献 Folium — Folium 0.19.5 documentationOffline Map Maker 离线地图下载 我们使用 Offline Map Maker 进行地图下载。 特别注意&#xff1a;Folium 默认支持 WGS84 坐标系&#xff0c;建议下载 WGS84 坐标系的地…

DeepSearch:字节新一代 DeerFlow 框架

项目地址&#xff1a;https://github.com/bytedance/deer-flow/ 【全新的 Multi-Agent 架构设计】独家设计的 Research Team 机制&#xff0c;支持多轮对话、多轮决策和多轮任务执行。与 LangChain 原版 Supervisor 相比&#xff0c;显著减少 Tokens 消耗和 API 调用次数&#…

Qt—用SQLite实现简单的注册登录界面

1.实现目标 本次实现通过SQLite制作一个简易的登录窗口&#xff0c;当点击注册按钮时&#xff0c;登录窗口会消失&#xff0c;会出现一个新的注册界面&#xff1b;完成注册或退出注册时&#xff0c;注册窗口会消失&#xff0c;重新出现登录窗口。注册过的用户信息会出现在SQLi…

day 18:零基础学嵌入式之数据结构——

一、基础内容 1.数据结构&#xff1a;相互之间存在一种或多种特定关系的数据元素的集合。 2.逻辑结构 &#xff08;1&#xff09;集合&#xff0c;所有数据在同一个集合中&#xff0c;关系平等。 &#xff08;2&#xff09;线性&#xff0c;数据和数据之间是一对一的关系 &am…

【SSL证书系列】客户端如何验证https网站服务器发的证书是否由受信任的根证书签发机构签发

客户端验证HTTPS网站证书是否由受信任的根证书颁发机构&#xff08;CA&#xff09;签发&#xff0c;是一个多步骤的过程&#xff0c;涉及证书链验证、信任锚&#xff08;Trust Anchor&#xff09;检查、域名匹配和吊销状态验证等。以下是详细的验证流程&#xff1a; 1. 证书链的…

iOS即时通信的技术要点

iOS即时通信开发的关键技术要点总结&#xff1a; 一、通讯协议选择 Socket通信 基础实现&#xff1a;使用原生BSD Socket或CFNetwork框架&#xff08;复杂&#xff09;&#xff0c;推荐第三方库如CocoaAsyncSocket&#xff08;封装GCDAsyncSocket&#xff09;&#xff0c;简化T…