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

news2025/5/19 15:40:36

一、栈

(一)栈的基本概念

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.coloradmin.cn/o/2378574.html

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

相关文章

RabbitMQ--进阶篇

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

Android Studio报错Cannot parse result path string:

前言 最近在写个小Demo&#xff0c;参考郭霖的《第一行代码》&#xff0c;学习DrawerLayout和NavigationView&#xff0c;不知咋地&#xff0c;突然报错Cannot parse result path string:xxxxxxxxxxxxx 反正百度&#xff0c;问ai都找不到答案&#xff0c;报错信息是完全看不懂…

关于网站提交搜索引擎

发布于Eucalyptus-blog 一、前言 将网站提交给搜索引擎是为了让搜索引擎更早地了解、索引和显示您的网站内容。以下是一些提交网站给搜索引擎的理由&#xff1a; 提高可见性&#xff1a;通过将您的网站提交给搜索引擎&#xff0c;可以提高您的网站在搜索结果中出现的机会。当用…

基于QT(C++)OOP 实现(界面)酒店预订与管理系统

酒店预订与管理系统 1 系统功能设计 酒店预订是旅游出行的重要环节&#xff0c;而酒店预订与管理系统中的管理与信息透明是酒店预订业务的关键问题所在&#xff0c;能够方便地查询酒店信息进行付款退款以及用户之间的交流对于酒店预订行业提高服务质量具有重要的意义。 针对…

机械元件杂散光难以把控?OAS 软件案例深度解析

机械元件的杂散光分析 简介 在光学系统设计与工程实践中&#xff0c;机械元件的杂散光问题对系统性能有着不容忽视的影响。杂散光会降低光学系统的信噪比、图像对比度&#xff0c;甚至导致系统功能失效。因此&#xff0c;准确分析机械元件杂散光并采取有效抑制措施&#xff0c…

游戏引擎学习第289天:将视觉表现与实体类型解耦

回顾并为今天的工作设定基调 我们正在继续昨天对代码所做的改动。我们已经完成了“脑代码&#xff08;brain code&#xff09;”的概念&#xff0c;它本质上是一种为实体构建的自组织控制器结构。现在我们要做的是把旧的控制逻辑迁移到这个新的结构中&#xff0c;并进一步测试…

【Linux网络】ARP协议

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

MUSE Pi Pro 开发板 Imagination GPU 利用 OpenCL 测试

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

多线程与线程互斥

我们初步学习完线程之后&#xff0c;就要来试着写一写多线程了。在写之前&#xff0c;我们需要继续来学习一个线程接口——叫做线程分离。 默认情况下&#xff0c;新创建的线程是joinable的&#xff0c;线程退出后&#xff0c;需要对其进行pthread_join操作&#xff0c;否则无法…

游戏引擎学习第287天:加入brain逻辑

Blackboard&#xff1a;动态控制类似蛇的多节实体 我们目前正在处理一个关于实体系统如何以组合方式进行管理的问题。具体来说&#xff0c;是在游戏中实现多个实体可以共同或独立行动的机制。例如&#xff0c;我们的主角拥有两个实体组成部分&#xff0c;一个是身体&#xff0…

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.摘要 随着空调负载在电力系统中所占比例的不断上升&#xff0c;其作为需求响应资源的潜力日益凸显。然而&#xff0c;由于建筑环境和用户行为的变化&#xff0c;空调负载具有异质性和…

.NET 中管理 Web API 文档的两种方式

前言 在 .NET 开发中管理 Web API 文档是确保 API 易用性、可维护性和一致性的关键。今天大姚给大家分享两种在 .NET 中管理 Web API 文档的方式&#xff0c;希望可以帮助到有需要的同学。 Swashbuckle Swashbuckle.AspNetCore 是一个流行的 .NET 库&#xff0c;它使得在 AS…

【HTML】个人博客页面

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

论文解读:ICLR2025 | D-FINE

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

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&#xff08;Generative Pre-trained Transformer&#xff09;是目前最广泛应用的大语言模型架构之一&#xff0c;其强大的自然语言理解与生成能力背后&#xff0c;是一个庞大而精细的训练流程。本文将从宏观到微观&#xff0c;系统讲解GPT的训练过程&#xff0c;…

python基础语法(三-中)

基础语法3&#xff1a; 2.列表与元组&#xff1a; <1>.列表、元组是什么&#xff1f; 都用来存储数据&#xff0c;但是两者有区别&#xff0c;列表可变&#xff0c;元组不可变。 <2>.创建列表&#xff1a; 创建列表有两种方式&#xff1a; [1].a 【】&#x…

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 展讯 T760 bl

ZTE 7551N 中兴小鲜60 远航60 努比亚小牛 解锁BL 刷机包 刷root 3款机型是一个型号&#xff0c;包通用&#xff0c; ro.product.system.modelZTE 7551N ro.product.system.nameCN_P720S15 #################################### # from generate-common-build-props # Th…

信息系统项目管理师高级-软考高项案例分析备考指南(2023年案例分析)

个人笔记整理---仅供参考 计算题 案例分析里的计算题就是进度、挣值分析、预测技术。主要考査的知识点有:找关键路径、求总工期、自由时差、总时差、进度压缩资源平滑、挣值计算、预测计算。计算题是一定要拿下的&#xff0c;做计算题要保持头脑清晰&#xff0c;认真读题把PV、…