栈和队列-介绍与实现(超级!!!详解-C语言)

目录

栈的介绍

        栈的概念

        栈的结构

栈的实现

        初始化栈 StackInit

        销毁栈 StackDestroy

        入栈 StackPush

        出栈 StackPop

        获取栈顶元素 StackTop

        检查栈是否为空 StackEmpty

        获取栈中有效元素个数 StackSize

队列

队列的介绍

        队列的概念

        队列的结构

        队列的应用

队列的实现

        初始化队列 QueueInit

        销毁队列 QueueDestroy

        队尾入队列 QueuePush

        队头出队列 QueuePop

        获取队列头部元素 QueueFront

        获取队尾尾部元素

        检查队列是否为空 QueueEmpty

        获取队列中有效元素个数 QueueSize


栈的介绍

        栈的概念

        栈:一种特殊的线性表,其只允许在固定的一端进行插入和删除元素操作。进行数据插入和删除操作的一端 称为栈顶,另一端称为栈底。栈中的数据元素遵守后进先出LIFO(Last In First Out)的原则。

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

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

        栈的结构

栈的实现

        初始化栈 StackInit

        需要用结构体创建一个栈,这个结构体需要包括栈的基本内容(栈,栈顶,栈的容量)。

typedef int STDataType;//栈中存储的元素类型(这里用整型举例)

typedef struct Stack
{
	STDataType* a;//栈
	int top;//栈顶
	int capacity;//容量,方便增容
}Stack;

        然后需要一个初始化函数,对刚创建的栈进行初始化。 

//初始化栈
void StackInit(Stack* pst)
{
	// 使用断言检查输入指针是否有效,确保不为空
    assert(pst);

    // 动态分配内存,为栈分配空间,初始可存储4个STDataType类型的元素
    pst->a = (STDataType*)malloc(sizeof(STDataType)* 4);

    // 初始化栈顶指针为0,表示栈当前为空,没有元素
    pst->top = 0;

    // 设置栈的初始容量为4,即栈目前最多可以容纳4个元素
    pst->capacity = 4;
}

        销毁栈 StackDestroy

        因为栈的内存空间是动态开辟出来的,当我们使用完后必须释放其内存空间,避免内存泄漏。

void StackDestroy(Stack* pst) 
{
    // 断言检查传入的栈结构体指针是否有效,确保它是指向一个已初始化的栈
    assert(pst != NULL);

    // 使用 free 函数释放栈中存储元素的数组所占用的内存防止内存泄漏
    free(pst->a);

    // 将栈的元素数组指针置为 NULL,这是一种安全措施,避免栈被释放后仍被当作有效指针使用
    pst->a = NULL;

    // 将栈顶指针 top 置为 0,这表示栈内已无任何元素,栈处于空状态
    pst->top = 0;

    // 将栈的容量 capacity 置为 0,代表栈当前没有存储能力,明确表示栈已经被销毁
    pst->capacity = 0;
}

        入栈 StackPush

        进行入栈操作前,我们需要检测栈的当前状态,若已满,则需要先对其进行增容,然后才能进行入栈操作。

void StackPush(Stack* pst, STDataType x) 
{

    // 断言检查栈结构体指针是否有效
    assert(pst);

    // 判断栈是否已满(栈顶索引等于栈的当前容量)
    if (pst->top == pst->capacity) 
    {
        // 如果栈满,则尝试重新分配内存,使栈容量翻倍
        STDataType* tmp =(STDataType*)realloc(pst->a,sizeof(STDataType)*pst->capacity * 2);

        // 检查重新分配内存是否成功
        if (tmp == NULL) 
        {
            // 若失败,输出错误信息,并调用 exit 函数结束程序运行
            printf("realloc fail\n");
            exit(-1);
        }

        // 若重新分配内存成功,更新栈的元素数组指针和容量
        pst->a = tmp;
        pst->capacity *= 2;
    }

    // 将新元素存入栈顶位置
    pst->a[pst->top] = x;

    // 更新栈顶索引,表示栈顶已移动至新的元素处
    pst->top++;
}

        出栈 StackPop

        出栈操作比较简单,即让栈顶的位置向下移动一位即可。但需检测栈是否为空,若为空,则不能进行出栈操作。

void StackPop(Stack* pst) 
{
    // 断言检查栈结构体指针是否有效
    assert(pst);

    // 断言检查栈是否为空,若为空则无法弹出元素
    assert(!StackEmpty(pst));

    // 栈顶指针向下移一位,相当于删除栈顶元素
    // 注意:这里假设移除元素后不需要返回其值,且不涉及内存释放
    pst->top--;

    // 注:在实际应用中,若栈顶元素包含动态分配的内存,此处还需要额外处理释放该内存
}

        获取栈顶元素 StackTop

        获取栈顶元素,即获取栈的最上方的元素。若栈为空,则不能获取。

STDataType StackTop(Stack* pst) 
{
    // 断言检查栈结构体指针是否有效
    assert(pst);

    // 断言检查栈是否为空,若为空则无法获取栈顶元素
    assert(!StackEmpty(pst));

    // 返回栈顶元素的值,栈顶元素的位置是栈顶指针减1
    // 注意:此处只读取栈顶元素而不改变栈的状态
    return pst->a[pst->top - 1];
}

        检查栈是否为空 StackEmpty

        检测栈是否为空,即判断栈顶的位置是否是0即可。若栈顶是0,则栈为空。

bool StackEmpty(Stack* pst) 
{
    // 断言检查栈结构体指针是否有效
    assert(pst);

    // 根据栈顶指针 top 的值判断栈是否为空
    // 当栈顶指针为 0 时,表示栈中没有元素,即栈为空
    return pst->top == 0;
}

        获取栈中有效元素个数 StackSize

        因为top记录的是栈顶,使用top的值便代表栈中有效元素的个数。

int StackSize(Stack* pst) 
{
    // 断言检查栈结构体指针是否有效
    assert(pst);

    // 栈顶指针 top 的值直接表示栈中有效元素的个数
    // 因为每当有元素入栈时,top加1;元素出栈时,top减1
    // 所以,top的值就反映了当前栈内实际存储了多少个元素
    return pst->top;
}

队列

队列的介绍

        队列的概念

        队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表。队列遵守先进先出 FIFO(First In First Out)的原则。

        入队列:队列的插入操作叫做入队列,进行插入操作的一端称为队尾。

        出队列:队列的删除操作叫做出队列,进行删除操作的一端称为队头。

        队列的结构

        队列的应用

        队列在生活中的运用非常广泛。很大一部分医院、营业厅以及餐厅等场所都存在队列的应用。
        例如,医院的采血环节的流程就运用了队列。在医院,如果我们要去抽血,我们首先要在旁边的抽号机抽取自己的编号,当某一个抽血窗口叫到你时,你便可以去抽血了。
        在这个过程中,当你在抽号机抽取到某一个编号,那么这个编号这时就从队尾入队列,当某一窗口抽血结束后,会在该队列中拿走一个编号并叫该编号的人到这个窗口接受抽血,那么这时这个编号就从队头出队列。

队列的实现

        首先我们需要创建一个结点类型,类型包含了该结点的数据和指向下一结点的指针。

typedef int QDataType;

typedef struct QListNode
{
    // 指向下一个节点的指针
    struct QListNode* next;
    
    // 节点所存储的数据
    QDataType data;
} QListNode;

        队列与普通链表又有所不同,普通链表只需要知道链表的头指针,而队列的信息包括了队头和队尾,所以我们需要再创建一个结构体用于存放队列的队头和队尾。 

typedef struct Queue
{
    // 队列头指针
    // 指向队列的第一个节点,当队列为空时,head 和 tail 都指向 NULL
    QListNode* head;

    // 队列尾指针
    // 指向队列的最后一个节点,每次有新元素入队时,都会更新 tail 指针
    QListNode* tail;
} Queue;

        初始化队列 QueueInit

         然后需要一个初始化函数,对刚创建的队列进行初始化。

void QueueInit(Queue* pq) 
{
    // 断言检查传入的队列结构体指针是否有效
    assert(pq);

    // 将队列的头指针 head 初始化为 NULL,表示队列当前为空,无元素
    pq->head = NULL;

    // 将队列的尾指针 tail 也初始化为 NULL,与 head 一致,表示队列为空
    // 这样设计的原因是,在队列为空时,头尾指针均指向 NULL;而在有元素入队时,tail 指针将指向最后一个元素
    pq->tail = NULL;
}

        销毁队列 QueueDestroy

        队列中的每一个结点所占用的内存空间都是动态开辟的,当我们使用完队列后需要及时释放队列中的每一个结点。

void QueueDestroy(Queue* pq) 
{
    // 断言检查传入的队列结构体指针是否有效
    assert(pq);

    // 创建一个临时指针 cur,初始化为队列的头节点
    QListNode* cur = pq->head;

    // 循环遍历队列中的每一个节点
    while (cur) 
    {
        // 将 cur 的下一个节点赋值给临时指针 next,保存下一个节点的地址
        QListNode* next = cur->next;

        // 释放当前节点 cur 的内存
        free(cur);

        // 移动 cur 指针至下一个节点
        cur = next;
    }

    // 清空队列的头指针和尾指针,将它们都设置为 NULL
    pq->head = NULL;
    pq->tail = NULL;
}

        队尾入队列 QueuePush

        入队列,即申请一个新结点并将其链接到队尾,然后改变队尾的指针指向即可。需要注意的是:若队列中原本无数据,那么我们只需让队头和队尾均指向这个新申请的结点即可。

void QueuePush(Queue* pq, QDataType x) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 分配内存创建一个新的节点
    QListNode* newnode = (QListNode*)malloc(sizeof(QListNode));
   
    // 检查内存分配是否成功
    if (newnode == NULL) 
    {
        // 若内存分配失败,输出错误信息并退出程序
        printf("malloc fail\n");
        exit(-1);
    }

    // 将新节点的数据域设置为要插入的元素值 x
    newnode->data = x;

    // 新节点的下一节点指针初始化为 NULL
    newnode->next = NULL;

    // 判断队列是否为空
    if (pq->head == NULL) 
    {
        // 若队列为空,则新节点既是头节点也是尾节点
        pq->head = pq->tail = newnode;
    } 
    else 
    {
        // 若队列非空,将现有尾节点的下一节点指向新节点
        pq->tail->next = newnode;
        
        // 更新队列尾指针,使之指向新插入的节点
        pq->tail = newnode;
    }
}

        队头出队列 QueuePop

        出队列,即释放队头指针指向的结点并改变队头指针的指向即可。若队列中只有一个结点,那么直接将该结点释放,然后将队头和队尾置空即可。

void QueuePop(Queue* pq) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 断言检查队列是否为空,若为空则不能进行出队操作
    assert(!QueueEmpty(pq));

    // 判断队列中剩余元素数量
    if (pq->head->next == NULL) 
    {
        // 如果队列只剩一个元素,则释放该元素,并将头尾指针都设为 NULL
        free(pq->head);
        pq->head = NULL;
        pq->tail = NULL;
    } 
    else 
    {
        // 如果队列中还有多个元素,则释放头节点,然后将头指针指向下一个节点
        QListNode* next = pq->head->next;
        free(pq->head);
        pq->head = next;
    }
}

        获取队列头部元素 QueueFront

        获取队列头部元素,即返回队头指针指向的数据即可。

QDataType QueueFront(Queue* pq) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 断言检查队列是否为空,若为空则无法获取队头元素
    assert(!QueueEmpty(pq));

    // 返回队列头部节点(即队首元素)的数据域值
    return pq->head->data;
}

        获取队尾尾部元素

        获取队列尾部元素,即返回队尾指针指向的数据即可。

QDataType QueueBack(Queue* pq) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 断言检查队列是否为空,若为空则无法获取队尾元素
    assert(!QueueEmpty(pq));

    // 返回队列尾部节点(即队尾元素)的数据域值
    return pq->tail->data;
}

        检查队列是否为空 QueueEmpty

        检测队列是否为空,即判断队头指针指向的内容是否为空。

bool QueueEmpty(Queue* pq) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 判断队列是否为空的条件是:头指针(pq->head)是否为 NULL
    // 如果头指针为 NULL,则队列为空,返回 true
    // 否则,队列非空,返回 false
    return pq->head == NULL;
}

        获取队列中有效元素个数 QueueSize

        队列中有效元素个数,即队列中的结点个数。我们只需遍历队列,统计队列中的结点数并返回即可。

int QueueSize(Queue* pq) 
{
    // 断言检查队列结构体指针是否有效
    assert(pq);

    // 初始化计数器 count 为 0
    int count = 0;

    // 创建一个临时指针 cur,初始化为队列的头节点
    QListNode* cur = pq->head;

    // 遍历队列,直到 cur 为空(即遍历完队列)
    while (cur) 
    {
        // 计数器加一,表示找到了一个有效节点
        count++;

        // 将 cur 指针移向下一个节点
        cur = cur->next;
    }

    // 返回计数器 count 的值,即队列中有效元素的个数
    return count;
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

上位机图像处理和嵌入式模块部署(树莓派4b用skynet实现进程通信)

【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing 163.com】 前面我们说过,在工业系统上面一般都是使用多进程来代替多线程。这后面,主要的原因还是基于安全的考虑。毕竟一个系统里面&a…

吴恩达机器学习笔记:第 8 周-13 聚类(Clustering)13.3-13.5

目录 第 8 周 13、 聚类(Clustering)13.3 优化目标13.4 随机初始化 第 8 周 13、 聚类(Clustering) 13.3 优化目标 K-均值最小化问题,是要最小化所有的数据点与其所关联的聚类中心点之间的距离之和,因此 K-均值的代价函数(又称畸变函数 Dis…

如何从架构层面降低公有云多可用区同时故障的概率

阿里云和腾讯云都曾出现过因一个组件故障而导致所有可用区同时瘫痪的情况。本文将探讨如何从架构设计的角度减小故障域,在故障发生时最小化业务损失,并以 Sealos 的稳定性实践为例,分享经验教训。 抛弃主从,拥抱点对点架构 从腾…

如何安全高效地进行网点文件下发?

随着IT技术的飞速发展,以银行为代表的企业数字化技术转型带来了大量的电子化文档传输需求。文件传输数量呈几何级数增长,传统集中式文件传输模式在爆炸式的增长需求下,银行网点文件下发的效率、可靠性、安全性等方面,都需要重点关…

Spring Boot:Web应用开发之增删改查的实现

Spring Boot 前言实现增删改查功能 前言 增删改查功能作为 Web 应用中的基础且重要的组成部分,是基本的数据库操作,也是实现业务逻辑和功能的关键要素。下面简单介绍使用 Spring Boot 实现增删改查的功能。 实现增删改查功能 在上一章 Spring Boot&am…

jvm(JVM快速入门、stack栈、堆、GC垃圾回收、Arthas)

文章目录 1. JVM快速入门1.1. 结构图1.2. 类加载器ClassLoader1.3. 执行引擎Execution Engine1.4. 本地接口Native Interface1.5. Native Method Stack1.6. PC寄存器(程序计数器)1.7. Method Area方法区 2. stack栈3. 堆3.1. 堆体系概述3.1.1. 新生区3.1.2. 老年代3.1.3. 永久代…

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测

分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测 目录 分类预测 | Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现SCSO-SVM沙猫群优化算法优化支持向量机多特征分类…

小程序AI智能名片商城系统直连:打造用户与企业无缝对接的新时代!

在高度不确定性的商业环境中,企业如何快速响应市场变化,实现与用户的零距离接触?答案就是——小程序AI智能名片商城系统直连!这一创新工具不仅为企业打开了与用户直接连接的大门,更为企业提供了持续收集用户反馈、快速…

AI图书推荐:如何用ChatGPT和Python进行数据可视化

《如何用ChatGPT和Python进行数据可视化》的原版英文图书标题:Python 3 Data Visualization Using ChatGPT - GPT-4 ,作者是 Oswald Campesato ,2023年出版 本书旨在向读者展示Python 3编程的概念和数据可视化的艺术。它还探讨了使用ChatGPT/…

vuetify3.0+tailwindcss+vite最新框架

1、根据vuetify官网下载项目 安装vuetify项目 2、根据tailwindcss官网添加依赖 添加tailwindcss依赖 3、 配置main.ts // main.ts import "./style.css"4、使用 <template><h1 class"text-3xl font-bold underline">Hello world!</…

SpringBoot学习之Kafka下载安装和启动【Windows版本】(三十四)

一、配置Java环境变量 打开CMD输入java -version检查java环境变量是否配置正确,如果配置正确在CMD窗口输入java -version应该输出如下: ​ 怎么配置Java环境变量这里我就不赘叙了,网上教程很多,请读者自行搜索操作。 二、下载Kafka 1、Kafka官网地址:Apache Kafka,…

C++进阶--异常

C语言传统的处理方式 终止程序&#xff1a;在发生错误时直接终止程序的运行&#xff0c;可以通过assert宏来进行实现。如assert(condition)&#xff0c;其中condition不满足要求时&#xff0c;将会使程序立刻停止执行&#xff0c;并输出相关错误信息。这种方式的确定是用户很难…

Golang基础3-函数、nil相关

函数 需要声明原型支持不定参数 func sum(numbers ...int)int支持返回多值支持递归支持命名返回参数 // 命名返回参数 func add(a, b int) (sum int) {sum a breturn // 这里不需要显式地写出返回值&#xff0c;因为已经在函数签名中声明了命名返回参数 } 支持匿名函数、闭包…

Jackson 2.x 系列【30】Spring Boot 集成之数据脱敏

有道无术&#xff0c;术尚可求&#xff0c;有术无道&#xff0c;止于术。 本系列Jackson 版本 2.17.0 本系列Spring Boot 版本 3.2.4 源码地址&#xff1a;https://gitee.com/pearl-organization/study-jaskson-demo 文章目录 1. 概述2. 实现思路3. 案例演示3.1 脱敏规则3.2 自…

图像处理之Retinex算法(C++)

图像处理之Retinex算法&#xff08;C&#xff09; 文章目录 图像处理之Retinex算法&#xff08;C&#xff09;前言一、单尺度Retinex&#xff08;SSR&#xff09;1.原理2.代码实现3.结果展示 二、多尺度Retinex&#xff08;MSR&#xff09;1.原理2.代码实现3.结果展示 三、带色…

Linux加强篇-存储结构与管理硬盘(一)

目录 ⛳️推荐 从“/”开始 物理设备命名规则 文件系统与数据资料 ⛳️推荐 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 从“/”开始 Linux系统中一切都是文件&#xff0c;都是从“…

deep learning

谷歌在线notebook 一、基本数据类型与用法 1.torch.tensor(张量) 按照维度不同(中括号的对数)&#xff0c;可以用torch.tensor创建scalar(标量)、vector(向量)、matrix(矩阵)&#xff0c; 一般的&#xff0c;一维是标量&#xff0c;二维是向量&#xff0c;三维是矩阵&#…

银河麒麟V10 SP1服务器客户端定时数据同步

银河麒麟V10 SP1服务器客户端定时数据同步 0.概述 当前只测试了将数据从客户端往服务端推送&#xff0c;两个客户端分别推送不同的数据 1.环境 三台电脑均为银河麒麟V10SP1桌面操作系统 服务器IP&#xff1a;192.168.1.51 用户名&#xff1a;wlh 客户端IP&#xff1a;192…

LabVIEW和MES系统的智能化车间数据对接

LabVIEW和MES系统的智能化车间数据对接 随着工业4.0时代的到来&#xff0c;智能制造成为推动制造业高质量发展的重要手段。其中&#xff0c;数字化车间作为智能制造的重要组成部分&#xff0c;其设计与实现至关重要。在数字化车间环境下&#xff0c;如何利用LabVIEW软件与MES系…

解析SoC芯片:构建智能设备的核心技术

✨✨ 欢迎大家来访Srlua的博文&#xff08;づ&#xffe3;3&#xffe3;&#xff09;づ╭❤&#xff5e;✨✨ &#x1f31f;&#x1f31f; 欢迎各位亲爱的读者&#xff0c;感谢你们抽出宝贵的时间来阅读我的文章。 我是Srlua小谢&#xff0c;在这里我会分享我的知识和经验。&am…