队列的讲解

队列的概念

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

一端进另一端出

也就是可以做到,先进先出

队列的使用场景

队列的经典应用就是保持公平性

比如:抽号机

生产者消费者模型,先来先出去,多线程的话就可能涉及到一个锁的概念

广度优先遍历(概念)

直接好友:好友

间接好友:好友的好友

可以采取队列的方式推荐

队列如何实现

数组的无法实现的因为需要一边进一边出,所以一般是单链表实现队列

同时单链表也会更加省空间

队列的实现 

创建文件

首先我们需要知道单链表实现队列的时候,我们需要有头节点和尾结点,以及链表的实际的个数,所以我们不能在结构体里面创建那麽多结构体,所以我们采取结构体的嵌套

创建栈的结构

// 链式结构:表示队列 
typedef struct QListNode
{QDataType _data;struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{QNode* _phead;//头节点QNode* _ptail;//尾结点int size;
}Queue;

这段代码定义了两个结构体,QNodeQueue,分别用于表示队列中的节点和整个队列。下面是对每个部分的详细解释:

  1. QNode 结构体:这个结构体表示队列中的单个节点。

    • _data:这是 QNode 结构体中的一个成员变量,用来存储节点中的数据。QDataType 是数据类型的别名,它应该在相关的头文件中定义,代表节点存储的数据类型。
    • _next:这是一个指向 QNode 类型的指针,用来指向同一队列中的下一个节点。如果 _next 为 NULL,表示这是队列中的最后一个节点。
  2. Queue 结构体:这个结构体表示整个队列。

    • _phead:这是一个指向 QNode 类型的指针,用来指向队列的头节点。队列的头节点是最早被加入的节点,也是下一个将要被移除的节点。
    • _ptail:这是一个指向 QNode 类型的指针,用来指向队列的尾节点。队列的尾节点是最后被加入的节点,它用于在添加新元素时更新队列。
    • size:这是一个整数类型的变量,用来存储队列中当前的元素数量。

在队列的链式表示中,不需要像顺序表示(使用数组)那样进行动态内存分配和缩容。链式结构自然地允许队列的动态增长和缩减,因为每个节点独立维护其后继节点的指针。

队列的基本操作包括:

  • 入队(Enqueue):在队尾添加一个新节点。
  • 出队(Dequeue):移除队头的节点,并返回其数据。
  • 查看队头(Peek/Front):返回队头节点的数据但不移除它。
  • 检查队列是否为空(IsEmpty):检查队列的头节点是否为 NULL

初始化与销毁队列

// 初始化队列 
void QueueInit(Queue* ps)
{ps->_phead = NULL;ps->_ptail = NULL;ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{assert(ps);QNode* cur = ps->_phead;while (cur){//存下下一个节点的地址,不会出现找空的情况QNode* next = cur->_next;free(cur);cur =next;}
}
  1. QueueInit 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • ps->_phead = NULL;:将队列的头节点指针设置为 NULL。这表示队列初始化时是空的,没有任何元素。
    • ps->_ptail = NULL;:将队列的尾节点指针也设置为 NULL。由于队列是空的,头尾节点都不存在。
    • ps->size = 0;:设置队列中元素的数量为0。
  2. QueueDestroy 函数

    • 这个函数同样接收一个指向 Queue 结构体的指针 ps
    • assert(ps);:使用 assert 宏来确保传入的 ps 不是 NULL。如果 ps 是 NULLassert 将触发断言失败,这有助于避免对空指针的无效操作。
    • QNode* cur = ps->_phead;:声明一个 QNode 类型的指针 cur 并初始化为队列的头节点。
    • while (cur):使用 while 循环来遍历队列,直到 cur 为 NULL,即队列中的所有节点都被处理完毕。
    • QNode* next = cur->_next;:在释放当前节点之前,先保存下一个节点的地址,以便在释放当前节点后能够继续遍历队列。
    • free(cur);:使用 free 函数释放当前节点 cur 所占用的内存。
    • cur = next;:将 cur 更新为下一个节点,继续循环直到所有节点都被释放。
    • 循环结束后,队列中的所有节点都被释放,此时队列被销毁。

队尾入队列 

// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("QueuePush:newnode:error:");exit(1);}//把需要插入的数值插入到节点里面newnode->_next = NULL;newnode->_data = x;// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail // 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL // 来检查队列中是否只有一个元素。//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较//ps->_phead == NULLif (ps->size == 0){ps->_phead = ps->_ptail = newnode;}else{//尾插节点ps->_ptail->_next = newnode;ps->_ptail= newnode;}ps->size++;
}

这段代码定义了一个名为 QueuePush 的函数,用于在队列的尾部添加一个新的元素。

  1. void QueuePush(Queue* ps, QDataType x):函数定义开始,QueuePush 接收一个指向 Queue 结构体的指针 ps 和一个要入队的新数据 x

  2. QNode* newnode = (QNode*)malloc(sizeof(QNode));:使用 malloc 函数为新节点分配内存。newnode 是指向新分配内存的指针。

  3. if (newnode == NULL):检查 malloc 是否成功分配了内存。如果 newnodeNULL,表示内存分配失败。

  4. perror("QueuePush:newnode:error:");:如果内存分配失败,使用 perror 函数输出错误信息。

  5. exit(1);:如果内存分配失败,调用 exit 函数以非零状态码退出程序。

  6. newnode->_next = NULL;:将新节点的 _next 指针设置为 NULL。这是新节点的尾部标识,表示在新节点之后没有更多的节点。

  7. newnode->_data = x;:将新数据 x 存储在新节点的 _data 成员中。

  8. if (ps->size == 0):检查队列是否为空(即队列中的元素数量为0)。

  9. ps->_phead = ps->_ptail = newnode;:如果是空队列,那么新节点同时是头节点和尾节点。因此,将头节点和尾节点指针都指向 newnode

  10. else:如果队列不是空的,执行以下操作:

    • ps->_ptail->_next = newnode;:将当前尾节点的 _next 指针指向新节点,从而将新节点添加到队列的末尾。
    • ps->_ptail = newnode;:更新尾节点指针,使其指向新节点。
  11. ps->size++;:队列中元素的数量增加1。

这段代码实现了队列的尾部入队操作。它首先检查队列是否为空,然后相应地将新节点添加到队列的末尾,并更新尾节点指针和队列的元素数量。如果内存分配失败,程序将输出错误信息并退出。

队头出队列 

// 队头出队列 
void QueuePop(Queue* ps)
{assert(ps && ps->size);// 改变头结点ps->_phead = ps->_phead->_next;ps->size--;
}

这段代码定义了一个名为 QueuePop 的函数,其目的是从队列头部移除一个元素。

  1. #include"Queue.h":这行代码应该位于文件的顶部,用于引入包含 Queue 结构体和 QNode 结构体定义以及 QDataType 类型定义的头文件。

  2. void QueuePop(Queue* ps):函数定义开始,QueuePop 接收一个指向 Queue 结构体的指针 ps 作为参数。

  3. assert(ps && ps->size);assert 是一个宏,用于在运行时测试一个条件是否为真。如果条件为假,则 assert 将产生一个断言失败,通常会导致程序异常终止。在这里,它用于确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素(即 ps->size 大于0)。

  4. ps->_phead = ps->_phead->_next;:这行代码将队列的头节点指针 _phead 更新为指向下一个节点,从而移除当前队列头部的节点。由于队列是先进先出(FIFO)的数据结构,队头节点是将要被移除的节点。

  5. ps->size--;:队列中元素的数量减少1。

这段代码实现了队列的头部出队操作。它首先通过 assert 检查队列是否非空,然后将头节点指针移动到下一个节点,以此出队操作,最后更新队列的元素数量。

获取队列头部元素

// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{assert(ps && ps->size);return ps->_phead->_data;
}
  1. QueueFront 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • assert(ps && ps->size);:使用 assert 宏确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素(即 ps->size 大于0)。
    • return ps->_phead->_data;:返回队列头部节点的 _data 成员。由于队列是先进先出(FIFO)的数据结构,队头节点的 _data 成员包含了最早被加入的元素的值。

获取队列队尾元素 

// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{assert(ps && ps->size);return ps->_ptail->_data;
}
  1. QueueBack 函数

    • 这个函数同样接收一个指向 Queue 结构体的指针 ps
    • assert(ps && ps->size);:使用 assert 宏确保传入的队列指针 ps 不是 NULL,并且队列中至少有一个元素。
    • return ps->_ptail->_data;:返回队列尾部节点的 _data 成员。在链式队列中,尾部节点是最后一个被加入的节点,其 _data 成员包含了最近一个被加入的元素的值。

获取队列中有效元素个数

// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{return ps->size;
}
  1. QueueSize 函数

    • 这个函数接收一个指向 Queue 结构体的指针 ps
    • return ps->size;:返回队列中有效元素的个数,即 size 成员的值。

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

// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{assert(ps);return ps->size == 0;
}

QueueEmpty 函数

  • 这个函数接收一个指向 Queue 结构体的指针 ps
  • assert(ps);:使用 assert 宏确保传入的队列指针 ps 不是 NULL
  • return ps->size == 0;:检查队列是否为空。如果 size 成员的值等于0,则表示队列为空,函数返回非零值(在C语言中,非零值被视为“真”),否则返回0(“假”)

 队列代码的总结 

Queue.h

#pragma once
#include<stdio.h>
#include<assert.h>
#include<stdlib.h>
typedef int QDataType;
// 链式结构:表示队列 
typedef struct QListNode
{QDataType _data;struct QListNode* _next;
}QNode;
// 队列的结构 (因为队列是先进先出,后进后出,也就是和栈是相反的,此时会尾进头出,所以我们需要更新尾部和头部节点)
// (把头结点,尾结点,链表的实际的大小放里面,这样不需要每次使用的时候进行循环找尾,我们只需要每次更新尾结点就可以)
typedef struct Queue
{QNode* _phead;//头节点QNode* _ptail;//尾结点int size;
}Queue;
// 这里采取一级指针进行实现代码逻辑,如果不创建队列的结构,我们就需要采取二级指针
// 初始化队列 
void QueueInit(Queue* ps);
// 销毁队列 
void QueueDestroy(Queue* ps);
// 队尾入队列 
void QueuePush(Queue* ps, QDataType data);
// 队头出队列 
void QueuePop(Queue* ps);
// 获取队列头部元素 
QDataType QueueFront(Queue* ps);
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps);
// 获取队列中有效元素个数 
int QueueSize(Queue* ps);
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps);

Queue.c

#include"Queue.h"
// 初始化队列 
void QueueInit(Queue* ps)
{ps->_phead = NULL;ps->_ptail = NULL;ps->size = 0;
}
// 销毁队列 
void QueueDestroy(Queue* ps)
{assert(ps);QNode* cur = ps->_phead;while (cur){//存下下一个节点的地址,不会出现找空的情况QNode* next = cur->_next;free(cur);cur =next;}
}
// 队尾入队列 
void QueuePush(Queue* ps, QDataType x)
{QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("QueuePush:newnode:error:");exit(1);}//把需要插入的数值插入到节点里面newnode->_next = NULL;newnode->_data = x;// ps->_phead == ps->_ptail 的结果确实是 true,这表明队列中只有一个元素(头尾相接)。// 但是,通常情况下,我们不会使用 ps->_phead == ps->_ptail // 来检查队列中是否只有一个元素。相反,我们通常会使用 ps->size == 1 或者 ps->_phead != NULL && ps->_ptail != NULL // 来检查队列中是否只有一个元素。//插入节点 第一个节点,这里的判断是不能写成ps->_phead ==ps->_ptail;因为都是空指针不能进行比较//ps->_phead == NULLif (ps->size == 0){ps->_phead = ps->_ptail = newnode;}else{//尾插节点ps->_ptail->_next = newnode;ps->_ptail= newnode;}ps->size++;
}
// 队头出队列 
void QueuePop(Queue* ps)
{assert(ps && ps->size);// 改变头结点ps->_phead = ps->_phead->_next;ps->size--;
}
// 获取队列头部元素 
QDataType QueueFront(Queue* ps)
{assert(ps && ps->size);return ps->_phead->_data;
}
// 获取队列队尾元素 
QDataType QueueBack(Queue* ps)
{assert(ps && ps->size);return ps->_ptail->_data;
}
// 获取队列中有效元素个数 
int QueueSize(Queue* ps)
{return ps->size;
}
// 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
int QueueEmpty(Queue* ps)
{assert(ps);return ps->size == 0;
}

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

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

相关文章

单调栈练习

最大矩形面积 如果栈为空&#xff0c;或者新的元素是大于栈顶元素的&#xff0c;那么新来的元素不会破坏栈的单调性&#xff0c;那么就把这个柱子入栈。 特别注意&#xff1a;这里的s.empty()和s.top().height < a不能调换顺序&#xff0c;包括后面的判断也要先判断栈是否为…

iOS 创建pch文件

1.参考链接&#xff08;xcode8添加方法&#xff0c;之前的跟这个差不多&#xff09;&#xff1a; 参考链接 2.自我总结&#xff1a; &#xff08;1&#xff09;创建pch文件: 注意点&#xff1a;1&#xff09;注意选中所有的targets&#xff08;看图明义&#xff09; 2&…

基于java 自定义注解Annotation设计简单ORM框架——进阶篇

目录 引言实例新建两个注解标注实体类拼接sql语句 总结 引言 一般Java规范编程&#xff08;只是一种习惯&#xff0c;而不是强制&#xff09;中&#xff0c;变量的命名方式一般采用驼峰式命名。比如userName&#xff0c;userImage。但是在数据库中一般不会采用驼峰式&#xff…

如何在 Ubuntu 12.10 上使用 Python 创建 Nagios 插件

介绍 Python 是一种在 Linux 上默认可用的流行命令处理器。 我们之前已经介绍过如何在 Ubuntu 12.10 x64 上安装 Nagios 监控服务器。 这一次&#xff0c;我们将扩展这个想法&#xff0c;使用 Python 创建 Nagios 插件。 这些插件将在客户 VPS 上运行&#xff0c;并通过 NR…

利用KMeans进行遥感NDWI进行聚类分割

&#xff08;1&#xff09;解释 KMeans算法是一种非监督式的聚类算法&#xff0c;于1967年由J. MacQueen提出&#xff0c;聚类的依靠是欧式距离&#xff0c;其核心思想就是将样本划分为几个类别&#xff0c;类里面的数据与类中心的距离最小。类的标签采用类里面样本的均值。 这…

Android Compose四: 常用的组件 Text

Text Composable fun Text(text: String, //用于设置显示文本modifier: Modifier Modifier, //设置形状大小点击事件等color: Color Color.Unspecified, //fontSize: TextUnit TextUnit.Unspecified,fontStyle: FontStyle? null,fontWeight: FontW…

动态规划-两个数组的dp问题3

文章目录 1. 两个字符串的最小ASCII删除和&#xff08;712&#xff09;2. 最长重复子数组&#xff08;718&#xff09; 1. 两个字符串的最小ASCII删除和&#xff08;712&#xff09; 题目描述&#xff1a; 状态表示&#xff1a; 根据经验以及题目要求&#xff0c;建立二维数…

数据结构与算法学习笔记三---栈和队列

目录 前言 一、栈 1.栈的表示和实现 1.栈的顺序存储表示和实现 1.C语言实现 2.C实现 2.栈的链式存储表示和实现 1.C语言实现 2.C实现 2.栈的应用 1.数制转换 二、队列 1.栈队列的表示和实现 1.顺序队列的表示和实现 2.链队列的表示和实现 2.循环队列 前言 这篇文…

JVM 自定义类加载器

文章目录 1. 为什么要自定义类加载器1.1 隔离加载类1.2 修改类加载的方式1.3 扩展加载源1.4 防止源码泄漏 2. 自定义类加载器应用场景有哪些3. 两种实现方式 自定义类加载器是Java中的一个高级特性&#xff0c;允许您在运行时动态加载类。通过自定义类加载器&#xff0c;您可以…

postman 使用教程

1. get 请求 &#xff1f;号后为 get 请求的参数 参数之间用符号"&" 分隔。 假设url 为&#xff1a;http://10.71.7.101/cgi-bin/gw-config.cgi?methodgetway_param&t1715658871647 复制进来到postman的地址栏 后 &#xff1f;后面的参数会自动添加到参…

JS中的宏任务和微任务

JavaScript 引擎是建立在一个事件循环系统之上的&#xff0c;它实时监控事件队列&#xff0c;如果有事件就执行&#xff0c;如果没有事件就等待。事件系统是一个典型的生产消费模式&#xff0c;生产者发出事件&#xff0c;接收者监听事件&#xff0c;在UI 开发中是常见的一个设…

OFDM802.11a的FPGA实现(十五)短训练序列:STS(含Matlab和verilog代码)

原文链接&#xff08;相关文章合集&#xff09;&#xff1a;OFDM 802.11a的xilinx FPGA实现 1.前言 在之前已经完成了data域数据的处理&#xff0c;在构建整个802.11a OFDM数据帧的时候&#xff0c;还剩下前导码和signal域的数据帧&#xff0c;这两部分的内容。 PLCP的前导部分…