链式结构实现队列

链式结构实现队列

    • 1.队列
      • 1.1队列的概念及结构
      • 1.2队列的实现
    • 2. 队列的各种函数实现
    • 3. 队列的全部代码实现

1.队列

1.1队列的概念及结构

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

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

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

如图所示:
在这里插入图片描述

1.2队列的实现

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。
在这里插入图片描述

2. 队列的各种函数实现

首先,我们先定义一个栈的结构

typedef int QDataType;//节点的结构
typedef struct QueueNode
{QDataType data;struct QueueNode* next;
}QNode;//链表的结构
typedef struct Queue
{QNode* phead;QNode* ptail;int size;
}Queue;

这里我们定义了两个结构体,这是因为在尾插的时候需要一个尾指针,如果每次尾插的时候都要遍历找尾效率太低,所以,我们可以定义一个尾指针,在头删的时候需要一个头指针,这个size其实可以加上有也可以不加,但是不加上的话求队列长度的时候需要遍历,比较麻烦,多个数据共同管理这个队列,所以,我们可以封装成一个结构体来管理这个队列。

初始化队列

void QueueInit(Queue* pq)
{aseert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}

销毁队列

void QueueDestroy(Queue* pq)
{assert(pq);QNode* pcur = pq->phead;while (pcur){QNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}

因为,队列中的每个节点的空间都是动态开辟的,所以在用完队列后要及时释放动态开辟的每一个节点。

队尾入队列

void QueuePush(Queue* pq,QDataType x)
{assert(pq);QNode* newcode = QueueBuyNode(x);if (pq->phead == NULL){assert(pq->ptail == NULL);//防止一个为空一个不为空的情况pq->phead = pq->ptail = newcode;}else{pq->ptail->next = newcode;pq->ptail = pq->ptail->next;}pq->size++;
}

这里也可以不把申请节点的代码单独写成一个函数,因为这里只有这一个地方会用到申请节点的操作,但是还是建议封装成一个函数,一方面可以提高代码的可读性,另一方面可以在一定程度上避免代码出错。

这里的assert(pq->ptail == NULL);这句代码主要时为了防止自己的代码写错,造成一个为空一个不为空的情况,所以也可以不写,保证写代码的时候细心一点就可以了。

申请一个节点

QNode* QueueBuyNode(QDataType x)
{c* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;
}

注意:这里是申请一个节点,所以要用定义队列节点的结构体类型 QNode,而不是管理队列的结构题类型

队头出队列

void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead->next == NULL){pq->phead = NULL;pq->ptail = NULL;}else{QNode* next = pq->phead->next;free(pq->phead);pq->phead =next;}pq->size--;
}

这里的队头出数据也就是头删操作,要注意当删除的链表元素只剩一个时,这里的头指针和尾指针同时指向这一个节点,当删除这个节点的时候,头指针和尾指针都会改变,所以,最好单独处理一下。

因为当只有一个节点的时候执行删除操作后,头指针会变成空,所以我们可以在头指针变成空的时候也把尾指针变成空,所以,这个代码也可以这样写。

void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));QNode* next = pq->phead->next;free(pq->phead);pq->phead = next;if (pq->phead == NULL){pq->ptail = NULL;}pq->size--;
}

获取队列头部元素

QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}

想要获取队头元素,只需要返回队头指针指向的元素的值

获取队列尾部元素

QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}

想要获取队尾元素,只需要返回队尾指针指向的元素的值

获取队列中有效元素个数

int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}

这里只需要返回控制队列结构里面的size就可以了,如果不把size封装结构体里面就需要遍历链表求队列元素个数,不但麻烦,效率也低。

检测队列是否为空,如果为空返回非零结果,如果非空返回0

bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL && pq->ptail == NULL;
}

当头指针和尾指针同时指向空的时候,链表一定为空。

3. 队列的全部代码实现

Queue.h

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int QDataType;//节点的结构
typedef struct QueueNode
{QDataType data;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);//获取队列中有效元素个数
int QueueSize(Queue* pq);//检测队列是否为空,如果为空返回非零结果,如果非空返回0 
bool QueueEmpty(Queue* pq);

Queue.c

#include "Queue.h"// 初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QNode* pcur = pq->phead;while (pcur){QNode* next = pcur->next;free(pcur);pcur = next;}pq->phead = NULL;pq->ptail = NULL;pq->size = 0;
}QNode* QueueBuyNode(QDataType x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail");exit(1);}newnode->data = x;newnode->next = NULL;return newnode;
}//队尾入队列
void QueuePush(Queue* pq,QDataType x)
{assert(pq);QNode* newcode = QueueBuyNode(x);if (pq->phead == NULL){assert(pq->ptail == NULL);pq->phead = pq->ptail = newcode;}else{pq->ptail->next = newcode;pq->ptail = pq->ptail->next;}pq->size++;
}// 队头出队列
void QueuePop(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));if (pq->phead->next == NULL){pq->phead = NULL;pq->ptail = NULL;}else{QNode* next = pq->phead->next;free(pq->phead);pq->phead =next;}pq->size--;
}//获取队列头部元素
QDataType QueueFront(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->phead->data;
}// 获取队列队尾元素
QDataType QueueBack(Queue* pq)
{assert(pq);assert(!QueueEmpty(pq));return pq->ptail->data;
}//获取队列中有效元素个数
int QueueSize(Queue* pq)
{assert(pq);return pq->size;
}bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL && pq->ptail == NULL;
}

Test.c

#include "Queue.h"int main()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);printf("size : %d\n",QueueSize(&q));while (!QueueEmpty(&q)){printf("%d ", QueueFront(&q));QueuePop(&q);}printf("\n");QueueDestroy(&q);return 0;
}

因为队列是后进先出的,所以要访问队列里面的元素时,必须要把队列当前元素取出才能访问下一个元素,每可以看到在打印队列里面的内容时,会把队列变成空,那么队列里面的内容就没有了,但时这也是实际当中的需求,所以不会有什么影响。

运行结果如图:
在这里插入图片描述

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

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

相关文章

2024年抖音小店还能做吗?想做好抖音小店就应该这样做!思路分享

大家好&#xff0c;我是电商花花。 新的一年又来给大家见面&#xff0c;今天花花先祝福大家大吉&#xff0c;新的一年里顺风又顺水。 现在已经正式进入2024年&#xff0c;那么就有一个老生常谈的问题了&#xff0c;就是2024年抖音小店还能做吗&#xff1f;会不会晚&#xff0…

面向对象编程(一)

目录 1. 面向对象编程概述(了解) 1.1 程序设计的思路 1.2 由实际问题考虑如何设计程序 2. Java语言的基本元素&#xff1a;类和对象 2.1 类和对象概述 2.2 类的成员概述 2.3面向对象完成功能的三步骤&#xff08;重要&#xff09; 步骤1&#xff1a;类的定义 步骤2&#xff1a;…

【STM32 CubeMX】I2C中断方式与DMA方式

文章目录 前言一、I2C中断方式1.1 CubeMX配置I2C中断1.2 I2C中断函数使用Master模式Mem模式 1.3 DMA方式发送和接收CubeMX配置IIC DMA方式Master模式Mem模式 总结 前言 在STM32 CubeMX环境中&#xff0c;I2C&#xff08;Inter-Integrated Circuit&#xff09;通信协议的实现可…

半导体物理基础-笔记

源内容参考&#xff1a;https://www.bilibili.com/video/BV11U4y1k7zn/?spm_id_from333.337.search-card.all.click&vd_source61654d4a6e8d7941436149dd99026962 半导体物理要解决的四个问题 载流子在哪里&#xff1b;如何获得足够多的载流子&#xff1b;载流子如何运动…

Day-02-02

Httpclient测试 安装HTTP Client插件 使用IDEA自带的http接口测试工具——HTTP Client Open in HTTP Client 生成测试用例 点击绿色箭头可以运行测试用例&#xff0c;控制台会输出结果。 保存和修改测试用例 在模块下新建一个api-test包用来存放测试用例&#xff0c;将生…

第7章 Page446~449 7.8.9智能指针 std::unique_ptr

“unique_ptr”是“独占式智能指针” 名字透露身份&#xff0c;“unique_ptr”是“独占式智能指针”。使用它管理前面的O类指针&#xff1a; 演示1&#xff1a; 例中 p 是一个智能指针。其中的“<O>”指明它所指向的数据类型是“O”。除了创建方法不太一样&#xff0c;…

【C->Cpp】由C迈向Cpp(3)

正文开始&#xff1a; 目录 &#xff08;一&#xff09;函数重载 &#xff08;1&#xff09;函数重载 &#xff08;2&#xff09;函数重载实现原理 &#xff08;二&#xff09; 引用 &#xff08;1&#xff09;引用 &#xff08;2&#xff09;语法 i &#xff0c;别名&am…

C#安装CommunityToolkit.Mvvm依赖

这里需要有一定C#基础&#xff0c; 首先找到右边的解决方案&#xff0c;右键依赖项 然后选择nuget管理 这里给大家扩展一下nuget的国内源&#xff08;https://nuget.cdn.azure.cn/v3/index.json&#xff09; 然后搜自己想要的依赖性&#xff0c;比如CommunityToolkit.Mvvm 再点…

Kibana:如何嵌入 Kibana 仪表板

作者&#xff1a;Carly Richmond 像我这样的前端工程师经常提出的要求是将 Kibana 等来源的现有仪表板嵌入到 JavaScript Web 应用程序中。 这是我必须多次执行的任务&#xff0c;因为我们希望快速部署用户生成的视图或允许用户控制给定的视图。 从我们从精彩的开发者社区收到的…

Git 初学

目录 一、需求的产生 二、版本控制系统理解 1. 认识版本控制系统 2. 版本控制系统分类 &#xff08;1&#xff09;集中式版本控制系统 缺点&#xff1a; &#xff08;2&#xff09;分布式版本控制系统 三、初识 git 四、git 的使用 例&#xff1a;将 “ OLED文件夹 ”…

每日一题 429.N叉树的层序遍历

429. N 叉树的层序遍历 描述&#xff1a; 给定一个 N 叉树&#xff0c;返回其节点值的层序遍历。&#xff08;即从左到右&#xff0c;逐层遍历&#xff09;。 树的序列化输入是用层序遍历&#xff0c;每组子节点都由 null 值分隔&#xff08;参见示例&#xff09;。 示例 1…

对待不合理需求,前端工程师如何优雅的say no!

曾经有位老板&#xff0c; 每次给前端提需求&#xff0c;前端都说实现不了&#xff0c;后来他搜索了一下&#xff0c;发现网上都有答案。他就在招聘要求上加了条&#xff1a;麻烦你在说不行的时候&#xff0c;搜索一下。 上面是一个段子&#xff0c;说的有点极端了&#xff0c;…