前言

李柱明博客:https://www.cnblogs.com/lizhuming/p/15487349.html

队列的定义

队列(queue)-- 只允许在一端进行插入操作,而在另一端进行删除操作的线性表:

  • FIFO:先进先出的线性表。
  • 允许插入的一端称为队尾,允许删除的一端称为队头。

注意:队列同样是线性表,也有类似线性表的各种操作。只是插入只能在队尾,删除只能在队头。

队列的抽象数据类型

队列的抽象数据类型可由两种实现:

  • 顺序队列:由数组或指针实现。
  • 链式队列:由链表是实现。

循环队列与链式队列对比

时间上:都是 O(1)。

空间上:

  • 循环队列:事先申请好空间,使用期间不释放。
  • 链队列:每次申请和释放结点也会存在一些时间开销。
  • 循环队列:固定长度,故存在存储元素个数和空间浪费的问题。
  • 链队列:需要指针域,产生一些空间上的开销,在空间上更灵活。

循环队列

特点

循环队列由数组实现。但是数组是有局限性的,所以循环队列有以下特点:

  1. 当队头固定设为数组下标为 0 的位置时:入队 O(1),但出队 O(n)。

  2. 当不限制队头必须在数组下标为 0 的位置时,可以提高一些性能。

    1. 需要引入两个下标。对头和队尾。
  3. 采用游标&循环方式:

    1. 引入两个指针,队头和队尾。
    2. 循环方式,即是如队尾溢出时便调到数组头继续。

定义

循环队列的定义:

  • 队列的头尾相接的顺序存储结构称为循环队列。

    • 可以理解为数组尾就是数组头,把头尾接驳。

循环队列相关计算

计算:

  • 队列 size:queue_size

  • 队列空判断:head == tail

  • 队列长度计算:(tail - head + QueueSize) % queue_size

  • 队列满判断:head == (tail + 1) % queue_size

    • 整个队列需要保留一个空的元素。
  • 入队:tail = (tail + 1) % queue_size

  • 出队:head = (head +1) % queue_size

链式队列

定义

链式队列:

  • 队列的链式存储结构,其实就是线性表的单链表,但只能尾进头出,简称链队列。
  • 本笔记的 demo 需要一个哨兵。即是头结点。哨兵为空节点(逻辑上不作为数据空间),有 queue->head 指向。
  • 队头指针指向链队列的头结点,队尾指针指向终端结点。
  • 空队列:head 和 tail 都指向头结点。

无哨兵链式队列:

有哨兵链式队列:

阻塞队列

阻塞队列:

  • 就是在队列基础上增加了阻塞操作。

  • 出队:当队列为空时,出队阻塞。

  • 入队:当队列满时,入队阻塞。

  • 可参考 FreeRTOS,采用链表方式维护被阻塞的线程。

    • 如有数据入队正常入队后,检查出队阻塞链表,阻塞链表中优先级最高的任务解除阻塞。

      • 把需要解除的任务的事件节点从消息队列的等待接收链表中移除。
      • 把需要解除的任务的状态节点从延时链表中移除,并插入到就绪链表中。
    • 若数据入队阻塞,则:

      • 把当前任务的事件节点插入到该队列的等待发送链表中。
      • 把当前任务的状态节点从就绪链表移除,并插入到延时链表中。(RTOS 会实时检查延时链表)
    • 注意:出队阻塞超时时,该任务会恢复就绪态,并在获得 CPU 权限后继续执行入队操作,看 API BaseType_t xQueueReceive(); 可知,恢复执行后便检查任务超时时间是否到期,若到期了,就把当前任务的事件节点从消息队列等待接收链表中移除,并返回错误码。

并发队列

并发队列:

  • 线程安全的队列叫作并发队列。
  • 可以通过锁机制实现线程安全,意思是给队列配个锁。
  • 但是锁粒度大并发度会比较低,同一时刻仅允许一个存或者取操作。
  • 基于数组的循环队列,利用 CAS(Compare And Swap) 原子操作,可以实现非常高效的并发队列。

代码实现

循环队列代码

/** @file         queue.c
* @brief 简要说明
* @details 详细说明
* @author lzm
* @date 2021-09-10 21:12:56
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
* @blog https://www.cnblogs.com/lizhuming/
*
**********************************************************
* @LOG 修改日志:
**********************************************************
*/ #include <stdio.h>
#include <stdlib.h>
#include <string.h> // 建议把本文件修改成循环队列的底层文件。
// 创建时,上层提供原数类型大小和最大个数即可。
// 而本文件的队列空间颗粒改为一个字节。 // 循环队列
typedef int qe_type; /* 元素类型 */
#define QUEUE_SIZE 100 /* 栈元素个数 */
typedef struct
{
qe_type data[QUEUE_SIZE]; /* 空间 */
int head; /* 头指针 */
int tail; /* 尾指针 */
}queue_array_t; /**
* @name queue_creat
* @brief
* @param
* @retval
* @author lzm
*/
queue_array_t *queue_array_creat(void)
{
queue_array_t *queue_ptr = NULL; queue_ptr = (queue_array_t *)malloc(sizeof(queue_array_t));
if(queue_ptr == NULL)
return NULL;
memset(queue_ptr, 0x00, sizeof(queue_array_t)); queue_ptr->head = 0;
queue_ptr->tail = 0; return queue_ptr;
} /**
* @name queue_destroy
* @brief
* @param
* @retval
* @author lzm
*/
int queue_destroy(queue_array_t *queue)
{
if(queue != NULL)
{
free(queue);
return 0;
} return -1;
} /**
* @name queue_clear
* @brief
* @param
* @retval
* @author lzm
*/
int queue_clear(queue_array_t *queue)
{
if(queue == NULL)
return -1; queue->head = 0;
queue->tail = 0; return 0;
} /**
* @name queue_empty
* @brief
* @param
* @retval
* @author lzm
*/
int queue_empty(queue_array_t *queue)
{
if(queue == NULL)
return -1; if(queue->head == queue->tail)
return 1; return 0;
} /**
* @name queue_full
* @brief
* @param
* @retval
* @author lzm
*/
int queue_full(queue_array_t *queue)
{
if(queue == NULL)
return -1; if(queue->head == (queue->tail + 1) % QUEUE_SIZE)
return 1; return 0;
} /**
* @name queue_length
* @brief
* @param
* @retval
* @author lzm
*/
int queue_length(queue_array_t *queue)
{
if(queue == NULL)
return -1; return (queue->tail - queue->head + QUEUE_SIZE) % QUEUE_SIZE;
} /**
* @name queue_insert
* @brief
* @param
* @retval
* @author lzm
*/
int queue_insert(queue_array_t *queue, qe_type elem)
{
if(queue_full(queue) != 0)
return -1; queue->data[queue->tail] = elem;
queue->tail = (queue->tail + 1) % QUEUE_SIZE; return 0;
} /**
* @name queue_delete
* @brief
* @param
* @retval
* @author lzm
*/
int queue_delete(queue_array_t *queue, qe_type *elem)
{
if(queue_empty(queue) != 0 || elem == NULL)
{
return -1;
} *elem = queue->data[queue->head];
queue->head = (queue->head + 1) % QUEUE_SIZE; return 0;
} /**
* @name queue_get_top
* @brief
* @param
* @retval
* @author lzm
*/
int queue_get_head(queue_array_t *queue, qe_type *elem)
{
if(queue_empty(queue) != 0 || elem == NULL)
{
return -1;
} *elem = queue->data[queue->head]; return 0;
}

链式队列实现

/** @file         queue.c
* @brief 简要说明
* @details 详细说明
* @author lzm
* @date 2021-09-10 21:31:11
* @version v1.0
* @copyright Copyright By lizhuming, All Rights Reserved
* @blog https://www.cnblogs.com/lizhuming/
*
**********************************************************
* @LOG 修改日志:
**********************************************************
*/ #include <stdio.h>
#include <stdlib.h>
#include <string.h> // 建议把本文件修改成循环队列的底层文件。
// 创建时,上层提供原数类型大小和最大个数即可。
// 而本文件的队列空间颗粒改为一个字节。 /* 链式结构 */
typedef int qe_type; /* 元素类型 */
typedef struct queue_node
{
qe_type date;
struct queue_node *next;
}queue_node_t; typedef struct
{
queue_node_t *head; /* 哨兵 */
queue_node_t *tail; /* 队尾 */
}queue_link_t; /**
* @name queue_link_creat
* @brief 使用了哨兵方式
* @param
* @retval
* @author lzm
*/
queue_link_t *queue_link_creat(int size)
{
queue_link_t *queue_ptr = NULL; queue_ptr = (queue_link_t *)malloc(sizeof(queue_link_t));
if(queue_ptr == NULL)
return NULL;
memset(queue_ptr, 0x00, sizeof(queue_link_t)); queue_ptr->tail = (queue_node_t *)malloc(sizeof(queue_node_t));
if(queue_ptr->tail == NULL)
{
return NULL;
}
queue_ptr->head = queue_ptr->tail; return queue_ptr;
} /**
* @name queue_link_destroy
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_destroy(queue_link_t *queue)
{
if(queue == NULL)
return -1; while(queue->head)
{
queue->tail = queue->head->next;
free(queue->head);
queue->head = queue->tail;
} free(queue); return 0;
} /**
* @name queue_link_clear
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_clear(queue_link_t *queue)
{
queue_node_t *queue_cur = NULL;
queue_node_t *queue_last = NULL; if(queue == NULL)
return -1; queue->tail = queue->head;
queue_cur = queue->head->next;
queue->head->next = NULL;
while(queue_cur)
{
queue_last = queue_cur;
queue_cur = queue_cur->next;
free(queue_last);
} return 0;
} /**
* @name queue_link_empty
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_empty(queue_link_t *queue)
{
if(queue == NULL)
return -1; if(queue->head == queue->tail)
return 1; return 0;
} /**
* @name queue_link_length
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_length(queue_link_t *queue)
{
int cnt = 0;
queue_node_t *queue_cur = NULL; if(queue == NULL)
return -1; queue_cur = queue->head;
while(queue_cur != queue->tail)
{
cnt++;
queue_cur = queue_cur->next;
} return cnt;
} /**
* @name queue_link_inster
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_inster(queue_link_t *queue, qe_type elem)
{
queue_node_t *queue_node_ptr = NULL; queue_node_ptr = (queue_node_t *)malloc(sizeof(queue_node_t));
if(queue_node_ptr == NULL)
return -1;
memset(queue_node_ptr, 0x00, sizeof(queue_node_t)); queue_node_ptr->date = elem;
queue_node_ptr->next = NULL;
queue->tail->next = queue_node_ptr;
queue->tail = queue_node_ptr; return 0;
} /**
* @name queue_link_delete
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_delete(queue_link_t *queue, qe_type *elem)
{
queue_node_t *node = NULL; if(queue_link_empty(queue) != 0 || elem == NULL)
{
return -1;
} node = queue->head->next;
*elem = node->date;
queue->head->next = node->next;
if(node == queue->tail)
queue->tail = queue->head;
free(node); return 0;
} /**
* @name queue_link_get_top
* @brief
* @param
* @retval
* @author lzm
*/
int queue_link_get_top(queue_link_t *queue, qe_type *elem)
{
if(queue_link_empty(queue) != 0 || elem == NULL)
{
return -1;
} *elem = queue->head->next->date; return 0;
}

【数据结构&算法】09-队列概念&参考源码的更多相关文章

  1. Java 集合系列 09 HashMap详细介绍(源码解析)和使用示例

    java 集合系列目录: Java 集合系列 01 总体框架 Java 集合系列 02 Collection架构 Java 集合系列 03 ArrayList详细介绍(源码解析)和使用示例 Java ...

  2. [算法1-排序](.NET源码学习)& LINQ & Lambda

    [算法1-排序](.NET源码学习)& LINQ & Lambda 说起排序算法,在日常实际开发中我们基本不在意这些事情,有API不用不是没事找事嘛.但必要的基础还是需要了解掌握. 排 ...

  3. 量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python)(转)

    量化交易中VWAP/TWAP算法的基本原理和简单源码实现(C++和python) 原文地址:http://blog.csdn.net/u012234115/article/details/728300 ...

  4. 延迟队列DelayQueue take() 源码分析

    延迟队列DelayQueue take() 源码分析 在工作中使用了延迟队列,对其内部的实现很好奇,于是就研究了一下其运行原理,在这里就介绍一下take()方法的源码 1 take()源码 如下所示 ...

  5. SpringBoot Jpa 双数据源mysql + oracle + liquibase+参考源码

    一.yml文件配置 spring: # 数据库配置 datasource: primary: jdbc-url: jdbc:mysql://localhost:3306/mes-dev?useUnic ...

  6. PyTorch--双向递归神经网络(B-RNN)概念,源码分析

    关于概念: BRNN连接两个相反的隐藏层到同一个输出.基于生成性深度学习,输出层能够同时的从前向和后向接收信息.该架构是1997年被Schuster和Paliwal提出的.引入BRNNS是为了增加网络 ...

  7. 【RabbitMQ学习记录】- 消息队列存储机制源码分析

    本文来自 网易云社区 . RabbitMQ在金融系统,OpenStack内部组件通信和通信领域应用广泛,它部署简单,管理界面内容丰富使用十分方便.笔者最近在研究RabbitMQ部署运维和代码架构,本篇 ...

  8. JAVA并发(7)-并发队列PriorityBlockingQueue的源码分析

    本文讲PriorityBlockingQueue(优先阻塞队列) 1. 介绍 一个无界的具有优先级的阻塞队列,使用跟PriorityQueue相同的顺序规则,默认顺序是自然顺序(从小到大).若传入的对 ...

  9. 10 DelayQueue 延时队列类——Live555源码阅读(一)基本组件类

    这是Live555源码阅读的第一部分,包括了时间类,延时队列类,处理程序描述类,哈希表类这四个大类. 本文由乌合之众 lym瞎编,欢迎转载 www.cnblogs.com/oloroso/ 本文由乌合 ...

随机推荐

  1. Python中类-带括号与不带括号的区别

    类不带括号我们叫赋值,带括号我们叫实例化. 什么是赋值? a=7 b=a id(7) 140726814208448 id(a) 140726814208448 id(b) 1407268142084 ...

  2. 集群环境下的Session管理

    1. 集群环境下的管理HTTPSSession所遇到的问题 一台服务器对应这个一个session对象,无法在另外一个服务器互通 解决方法: 1. Session 的 Replication(复制)将当 ...

  3. [转载]session多服务器共享的方案梳理

    转载网址: http://www.cnblogs.com/wangtao_20/archive/2013/10/29/3395518.html session的存储了解以前是怎么做的,搞清楚了来龙去脉 ...

  4. P5540-[BalkanOI2011]timeismoney|最小乘积生成树【最小生成树,凸壳】

    正题 题目链接:https://www.luogu.com.cn/problem/P5540 题目大意 给出\(n\)个点\(m\)条边边权是一个二元组\((a_i,b_i)\),求出一棵生成树最小化 ...

  5. CF585E-Present for Vitalik the Philatelist【莫比乌斯反演,狄利克雷前缀和】

    正题 题目链接:https://www.luogu.com.cn/problem/CF585E 题目大意 给出一个大小为\(n\)的可重集\(T\),求有多少个它的非空子集\(S\)和元素\(x\)满 ...

  6. Pycharm新建模板默认添加作者时间等信息(逼格更高,好像很历害的样子)

    在pycharm使用过程中,关于代码编写者的一些个人信息快捷填写,使用模板的方式比较方便. 方法如下: 1.打开pycharm,选择File-Settings 2.选择Editor--Color&am ...

  7. Kettle学习笔记(二)— 基本操作

    目录 Kettle学习笔记(一)- 环境部署及运行 Kettle学习笔记(二)- 基本操作 kettle学习笔记(三)- 定时任务的脚本执行 Kettle学习笔记(四)- 总结 打开Kettle 打开 ...

  8. Semi-supervised semantic segmentation needs strong, varied perturbations

    论文阅读: Semi-supervised semantic segmentation needs strong, varied perturbations 作者声明 版权声明:本文为博主原创文章,遵 ...

  9. 订单峰值激增 230%,Serverless 如何为世纪联华降本超 40%?|双11 云原生实践

    作者 | 朱鹏 导读:2020 年 双11,世纪联华基于阿里云函数计算 (FC) 弹性扩容,应用于大促会场 SSR.线上商品秒杀.优惠券定点发放.行业导购.数据中台计算等多个场景,业务峰值 QPS 较 ...

  10. Java秘诀!Java逻辑运算符介绍

    运算符丰富是 Java 语言的主要特点之一,它提供的运算符数量之多,在高级语言中是少见的. Java 语言中的运算符除了具有优先级之外,还有结合性的特点.当一个表达式中出现多种运算符时,执行的先后顺序 ...