数据结构C语言描述5(图文结合)--队列,数组、链式、优先队列的实现

前言

  • 这个专栏将会用纯C实现常用的数据结构和简单的算法;
  • 有C基础即可跟着学习,代码均可运行;
  • 准备考研的也可跟着写,个人感觉,如果时间充裕,手写一遍比看书、刷题管用很多,这也是本人采用纯C语言实现的原因之一;
  • 欢迎收藏 + 关注,本人将会持续更新。

文章目录

    • 什么是队列
    • 队列基本操作
    • 队列的数组实现
    • 队列的链表实现
    • 双端队列
    • 优先队列简介

什么是队列

队列(Queue)也是一种运算受限的线性表,它限定在表的一端进行插入操作,在表的另一端进行删除操作。队列的结构特性是:先进先出FIFO ( First In First Out)。队列又被称为先进先出表。

队尾:允许插入的一端称作队尾(Rear)

队首:允许删除的一端称作队首(Front)

在这里插入图片描述

队列为空的时候 队头front和队尾tail都是0的位置,入队的时候队尾tail往后移动,出队的时候front往队列tail靠拢

因为front的移动,导致数组队列存在伪溢出现象,可以通过循环队列的方式解决伪溢出问题。

伪溢出:不是实际的的内存越界,只是队头下标比队尾下标前。

在这里插入图片描述

链式队列可以通过无表头链表记录头尾的方式实现,插入队列用表尾法插入,遍历表头法删除写法

队列基本操作

  • 创建栈
  • 入队
  • 出队
  • 获取队头元素
  • 队列是否为空
  • 队列元素个数

队列的数组实现

数组队列,就是数组模拟队列,实现方法有很多,这里也只是一种方法。

🚸队列封装

  • 数组队列实现是用过移动队头、队尾下标实现的,故核心在于front、tail的理解。
  • 数组采用扩容的方法存储数组。
typedef int DataType;
typedef struct Queue {DataType* data;int front, tail;int capacity;
}Queue;

🖍 创建队列(初始化)

这一步就是创建队列,为队列申请一块内存,并且初始化队列,注意,这里需要将tail赋值为-1 为什么呢? 这个可以随着看代码体会。

Queue* create_queue()
{Queue* queue = (Queue*)calloc(1, sizeof(Queue));assert(queue);queue->tail = -1;return queue;
}

🌓 入队

  • ++tail,采用后置++的方法从队尾巴插入,这里应该就能理解为什么要将tail赋值为-1
  • 注意:容量不够需要扩容。
void push(Queue* queue, DataType data)
{assert(queue);// 扩容if (queue->front == -1 || (queue->front >= queue->capacity)) {DataType* temp = (DataType*)realloc(queue->data, queue->capacity + 10);assert(temp);queue->data = temp;queue->capacity += 10;}queue->data[++queue->tail] = data;
}

✴️ 出队

  • 就是移动front,因为数组无法单独删除一个元素;
  • 注意:队列为空的情况,front>tail
void pop(Queue* queue)
{assert(queue);if (queue->front <= queue->tail) {queue->front++;}
}

🤕 获取对头元素

  • 这个就是通过数组下标直接获取即可。
DataType top(Queue* queue)
{assert(queue);return queue->data[queue->front];
}

🚄 万金油函数:队列大小、是否为空

  • 获取大小:注意获取的是大小,不是下标;
  • 判断是否为空:就是对头和队尾的下标对比。
int size(Queue* queue)
{assert(queue);return queue->tail + 1;
}bool empty(Queue* queue)
{assert(queue);return queue->tail < queue->front;
}

⚗️ 总代码

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>typedef int DataType;typedef struct Queue {DataType* data;int front, tail;int capacity;
}Queue;Queue* create_queue()
{Queue* queue = (Queue*)calloc(1, sizeof(Queue));assert(queue);queue->tail = -1;return queue;
}void push(Queue* queue, DataType data)
{assert(queue);// 扩容if (queue->front == -1 || (queue->front >= queue->capacity)) {DataType* temp = (DataType*)realloc(queue->data, queue->capacity + 10);assert(temp);queue->data = temp;queue->capacity += 10;}queue->data[++queue->tail] = data;
}DataType top(Queue* queue)
{assert(queue);return queue->data[queue->front];
}void pop(Queue* queue)
{assert(queue);if (queue->front <= queue->tail) {queue->front++;}
}int size(Queue* queue)
{assert(queue);return queue->tail + 1;
}bool empty(Queue* queue)
{assert(queue);return queue->tail < queue->front;
}int main()
{Queue* queue = create_queue();for (int i = 1; i <= 10; i++) {push(queue, i);}while (!empty(queue)) {printf("%d ", top(queue));pop(queue);}return 0;
}

队列的链表实现

链表这里采用的是无头单链表实现,其中:

  • 入栈:插入队头
  • 出栈:弹出队头

🌛 队列封装

这个部分就是正常的节点封装,队列封装。

// 无头链表,封装写法,尾插法typedef int DataType;typedef struct Node {DataType* data;struct Node* next;
}Node;typedef struct Queue {Node* queueHead;// Node* tailHead;     // 添加这个节点会更简单一点int size;
}Queue;

👤 创建队列(初始化)

这一部分也是正常的创建节点、队列,然后申请内存。

Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Queue* create_queue()
{Queue* queue = (Queue*)calloc(1, sizeof(Queue));assert(queue);return queue;
}

📌 入队

入队就是在链表头插入,但是要注意,再插入的时候需要判断是否为空表:

  • 空表:插入节点作为头;
  • 不为空,则头插。
void push(Queue* queue, DataType data)
{assert(queue);if (queue->size == 0) {queue->queueHead = create_node(data);}else {Node* temp = queue->queueHead;while (temp->next) {temp = temp->next;}temp->next = create_node(data);}queue->size++;
}

🏤 出队

出队就是弹出头节点,但是要注意提前判断链表是否为空的情况。

void pop(Queue* queue)
{assert(queue);if (queue->size == 0) {return;}Node* temp = queue->queueHead;queue->queueHead = temp->next;free(temp);temp = NULL;queue->size--;
}

🍿 获取对头元素

这个就是获取头节点的元素值,要注意的是空表的情况,这里空表是直接断言了。

DataType top(Queue* queue)
{assert(queue);assert(queue->size != 0);   // 队列为空,不能获取栈顶元素return queue->queueHead->data;
}

🔌 万金油函数:队列大小、是否为空

这个没有什么好说的,看代码吧。

bool empty(Queue* queue)
{assert(queue);return queue->size == 0;
}int size(Queue* queue)
{assert(queue);return queue->size;
}

总代码

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>// 无头链表,封装写法,尾插法typedef int DataType;typedef struct Node {DataType* data;struct Node* next;
}Node;typedef struct Queue {Node* queueHead;// Node* tailHead;     // 添加这个节点会更简单一点int size;
}Queue;Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Queue* create_queue()
{Queue* queue = (Queue*)calloc(1, sizeof(Queue));assert(queue);return queue;
}void push(Queue* queue, DataType data)
{assert(queue);if (queue->size == 0) {queue->queueHead = create_node(data);}else {Node* temp = queue->queueHead;while (temp->next) {temp = temp->next;}temp->next = create_node(data);}queue->size++;
}DataType top(Queue* queue)
{assert(queue);assert(queue->size != 0);   // 队列为空,不能获取栈顶元素return queue->queueHead->data;
}void pop(Queue* queue)
{assert(queue);if (queue->size == 0) {return;}Node* temp = queue->queueHead;queue->queueHead = temp->next;free(temp);temp = NULL;queue->size--;
}bool empty(Queue* queue)
{assert(queue);return queue->size == 0;
}int size(Queue* queue)
{assert(queue);return queue->size;
}int main()
{Queue* queue = create_queue();for (int i = 1; i <= 10; i++) {push(queue, i);}while (!empty(queue)) {printf("%d ", top(queue));pop(queue);}return 0;
}

双端队列

双端队列(Deque)是一种具有队列和栈性质的数据结构,它允许我们从两端添加或删除元素。这种灵活性使得双端队列在多种场景下都非常有用。

双端队列支持的基本操作包括

  • 入队:在队列的前端或后端添加元素。
  • 出队:从队列的前端或后端移除元素。
  • 访问:访问队列的前端或后端元素而不移除它们。

🉑 实现

  • 采用无头双向链表实现

📦 节点封装

这个就是封装数据节点,双向链表节点,和之前一样,代码如下:

typedef int DataType;typedef	struct Node {DataType data;struct Node* prev;struct Node* next;
}Node;typedef struct Deque {Node* head;Node* tail;int count;
}Deque;

📇 初始化节点

这个也是和上面一样,封装创建节点、创建队列的节点。

Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Deque* create_deque()
{Deque* deque = (Deque*)calloc(1, sizeof(Deque));assert(deque);return deque;
}

🎧 头插

头插这个要注意的就是判断链表是否为空。

void push_front(Deque* deque, DataType data)
{assert(deque);if (deque->count == 0) {deque->head = deque->tail = create_node(data);}else {Node* node = create_node(data);node->next = deque->head;deque->head->prev = node;deque->head = node;}deque->count++;
}

🎉 尾插

尾插这个也是要注意的就是判断链表是否为空。

void push_back(Deque* deque, DataType data)
{assert(deque);if (deque->count == 0) {deque->head = deque->tail = create_node(data);}else {Node* node = create_node(data);node->prev = deque->tail;deque->tail->next = node;deque->tail = node;}deque->count++;
}

👟 头删

删除要注意几个点:

  • 空不能删;
  • 删除后,如果是一个节点删除,则要将指向尾节点指针赋值为NULL,如果不是,则需要将新的对头元素前指针赋值为NULL。
void pop_front(Deque* deque)
{assert(deque);assert(deque->count != 0);   // 空,不能弹出Node* temp = deque->head;deque->head = temp->next;free(temp);temp = NULL;(deque->head) ? (deque->head->prev = NULL) : (deque->tail = NULL);   // 这个写法deque->count--;
}

🚖 尾删

删除要注意几个点:

  • 空不能删;
  • 删除后,如果是一个节点删除,则要将指向头节点指针赋值为NULL,如果不是,则需要将新队尾的下一个指针赋值为NULL。
void pop_tail(Deque* deque)
{assert(deque);assert(deque->count != 0);Node* temp = deque->tail;deque->tail = temp->prev;(deque->tail) ? (deque->tail->next = NULL) : (deque->head = NULL);free(temp);temp = NULL;deque->count--;
}

📑 获取对头、队尾大小

这个就是获取对于节点的元素值,但是要注意的是没有元素的情况。

DataType top_front(Deque* deque)
{assert(deque);assert(deque->count != 0);return deque->head->data;
}DataType top_tail(Deque* deque)
{assert(deque);assert(deque->count != 0);return deque->tail->data;
}

🆎 总代码

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <assert.h>/*
C++: deque
实现:双向链表
*/typedef int DataType;typedef	struct Node {DataType data;struct Node* prev;struct Node* next;
}Node;typedef struct Deque {Node* head;Node* tail;int count;
}Deque;Node* create_node(DataType data)
{Node* node = (Node*)calloc(1, sizeof(Node));assert(node);node->data = data;return node;
}Deque* create_deque()
{Deque* deque = (Deque*)calloc(1, sizeof(Deque));assert(deque);return deque;
}void push_back(Deque* deque, DataType data)
{assert(deque);if (deque->count == 0) {deque->head = deque->tail = create_node(data);}else {Node* node = create_node(data);node->prev = deque->tail;deque->tail->next = node;deque->tail = node;}deque->count++;
}void push_front(Deque* deque, DataType data)
{assert(deque);if (deque->count == 0) {deque->head = deque->tail = create_node(data);}else {Node* node = create_node(data);node->next = deque->head;deque->head->prev = node;deque->head = node;}deque->count++;
}// 简单代码的思路,记住
void pop_front(Deque* deque)
{assert(deque);assert(deque->count != 0);   // 空,不能弹出Node* temp = deque->head;deque->head = temp->next;free(temp);temp = NULL;(deque->head) ? (deque->head->prev = NULL) : (deque->tail = NULL);   // 这个写法deque->count--;
}void pop_tail(Deque* deque)
{assert(deque);assert(deque->count != 0);Node* temp = deque->tail;deque->tail = temp->prev;(deque->tail) ? (deque->tail->next = NULL) : (deque->head = NULL);free(temp);temp = NULL;deque->count--;
}DataType top_front(Deque* deque)
{assert(deque);assert(deque->count != 0);return deque->head->data;
}DataType top_tail(Deque* deque)
{assert(deque);assert(deque->count != 0);return deque->tail->data;
}void print_deque(Deque* deque)
{assert(deque);Node* temp = deque->head;while (temp != NULL) {printf("%d ", temp->data);temp = temp->next;}printf("\n");
}int main()
{Deque* deque = create_deque();for (int i = 1; i <= 10; i++) {push_front(deque, i);}print_deque(deque);for (int i = 20; i <= 30; i++) {push_back(deque, i);}print_deque(deque);pop_front(deque);pop_tail(deque);print_deque(deque);return 0;
}

优先队列简介

优先队列,我们第一反应是,没错,应该是应用最广泛的优先队列,但是从优先队列的定义来看,堆也只是其一种实现方式,优先队列的定义是:按照权值出队。

这里实现了一个简单的优先队列,出队的时候按照优先权最高出队,代码如下:

#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <assert.h>// 优先队列,按照权值出队,这里只是实现一个简易版本
#define MAX 100typedef struct Data {int key;    // 比较准则char name[20];
}Data;typedef struct PriQueue {Data data[MAX];    // 简单版本,不扩容int curSize;
}PriQueue;PriQueue* create_priqueue()
{PriQueue* queue = (PriQueue*)calloc(1, sizeof(PriQueue));assert(queue);queue->curSize = -1;  /// 头为 -1return queue;
}bool empty(PriQueue* queue)
{assert(queue);return queue->curSize == -1;
}size_t size(PriQueue* queue)
{assert(queue);return queue->curSize + 1;
}void push(PriQueue* queue, Data data)
{assert(queue);if (queue->curSize == MAX)return;queue->data[++queue->curSize] = data;
}// 这里规定:权重最大的优先权最高,故弹出优先权最大的
void pop(PriQueue* queue, Data* temp)   // temp:存储需弹出的元素
{assert(queue);Data t = queue->data[0];int max = 0;for (int i = 1; i <= queue->curSize; i++) {if (queue->data[i].key > t.key) {t = queue->data[i];max = i;}}// 存储*temp = queue->data[max];for (int i = max; i <= queue->curSize - 1; i++) {queue->data[i] = queue->data[i + 1];}queue->curSize--;
}int main()
{PriQueue* queue = create_priqueue();Data arr[5] = { {1,"小美"},{5,"小丽"},{3,"小芳"},{4,"coco"},{2,"花花"} };for (int i = 0; i < 5; i++) {push(queue, arr[i]);}while (!empty(queue)) {Data temp;pop(queue, &temp);printf("key: %d, value: %s\n", temp.key, temp.name);}return 0;
}

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

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

相关文章

一篇文章了解机器学习

一篇文章了解机器学习&#xff08;上&#xff09; 一、软件版本安装二、数据集的加载三、数据集的切分四、数据特征提取及标准化1、字典数据的特征提取2、文本特征向量的提取3、数据标准化处理 四、特征降维注&#xff1a;训练器的区别&#xff1a;&#xff1a;五、模型的训练与…

day03(单片机高级)RTOS

目录 RTOS(实时操作系统) 裸机开发模式 轮询方式 前后台&#xff08;中断方式&#xff09; 改进&#xff08;前后台&#xff08;中断&#xff09;&#xff09;定时器 裸机进一步优化 裸机的其他问题 RTOS的概念 什么是RTOS 为什么要使用 RTOS RTOS的应用场景 RTOS的…

Hello-Go

Hello-Go 环境变量 GOPATH 和 GOROOT &#xff1a;不同于其他语言&#xff0c;go中没有项目的说法&#xff0c;只有包&#xff0c;其中有两个重要的路径&#xff0c;GOROOT 和 GOPATH Go开发相关的环境变量如下&#xff1a; GOROOT&#xff1a;GOROOT就是Go的安装目录&…

pytorch官方FasterRCNN代码详解

本博文转自捋一捋pytorch官方FasterRCNN代码 - 知乎 (zhihu.com)&#xff0c;增加了其中代码的更详细的解读&#xff0c;以帮助自己理解该代码。 代码理解的参考Faster-RCNN全面解读(手把手带你分析代码实现)---前向传播部分_手把手faster rcnn-CSDN博客 1. 代码结构 作为 to…

全志T113双核异构处理器的使用基于Tina Linux5.0——RTOS系统定制开发

8、RTOS系统定制开发 此处以在rtos/components/aw目录下创建一个简单的软件包为例&#xff0c;帮助客户了解RTOS环境&#xff0c;为RTOS系统定制开发提供基础。 RTOS环境下的软件包主要由三部分组成&#xff0c;源文件&#xff0c;Makefile&#xff0c;Kconfig&#xff0c;如下…

springboot实战(13)(@PatchMapping、@RequestParam、@URL、ThreadLocal线程局部变量)

目录 一、PATCH请求方式。 二、实现用户更新头像功能。 三、注解RequestParam。 四、注解URL。&#xff08;对传来的参数是否是合法地址进行校验&#xff09; 一、PATCH请求方式。 patch中文翻译&#xff1a;局部、小块。PATCH 请求主要用于对已存在的资源进行局部修改&#xf…

nvm安装node遇到的若干问题(vscode找不到npm文件、环境变量配置混乱、npm安装包到D盘)

问题一&#xff1a;安装完nvm后需要做哪些环境变量的配置&#xff1f; 1.打开nvm文件夹下的setting文件&#xff0c;设置nvm路径和安装node路径&#xff0c;并添加镜像。 root: D:\software\nvm-node\nvm path: D:\software\nvm-node\nodejs node_mirror: https://npmmirror.c…

面向FWA市场!移远通信高性能5G-A模组RG650V-NA通过北美两大重要运营商认证

近日&#xff0c;全球领先的物联网整体解决方案供应商移远通信宣布&#xff0c;其旗下符合3GPP R17标准的新一代5G-A模组RG650V-NA成功通过了北美两家重要运营商认证。凭借高速度、大容量、低延迟、高可靠等优势&#xff0c;该模组可满足CPE、家庭/企业网关、移动热点、高清视频…

2024年11月21日Github流行趋势

项目名称&#xff1a;twenty 项目维护者&#xff1a;charlesBochet, lucasbordeau, Weiko, FelixMalfait, bosiraphael项目介绍&#xff1a;正在构建一个由社区支持的现代化Salesforce替代品。项目star数&#xff1a;21,798项目fork数&#xff1a;2,347 项目名称&#xff1a;p…

AWTK 最新动态:支持鸿蒙系统(HarmonyOS Next)

HarmonyOS是全球第三大移动操作系统&#xff0c;有巨大的市场潜力&#xff0c;在国产替代的背景下&#xff0c;机会多多&#xff0c;AWTK支持HarmonyOS&#xff0c;让AWTK开发者也能享受HarmonyOS生态的红利。 AWTK全称为Toolkit AnyWhere&#xff0c;是ZLG倾心打造的一套基于C…

docker 配置同宿主机共同网段的IP 同时通过通网段的另一个电脑实现远程连接docker

docker配置网络 #宿主机执行命令 ifconfig 查询对应的主机ip 子网掩码 网关地址 #[网卡名称]&#xff1a;inet[主机IP] netmask[子网掩码] broadcast[网关地址]这里需要重点关注&#xff1a;eno1[网卡名称]以及【192.168.31.225】网关地址 在宿主机执行docker命令创建一个虚拟…

使用 Elastic AI Assistant for Search 和 Azure OpenAI 实现从 0 到 60 的转变

作者&#xff1a;来自 Elastic Greg Crist Elasticsearch 推出了一项新功能&#xff1a;Elastic AI Assistant for Search。你可以将其视为 Elasticsearch 和 Kibana 开发人员的内置指南&#xff0c;旨在回答问题、引导你了解功能并让你的生活更轻松。在 Microsoft AI Services…

React (三)

文章目录 项目地址十二、性能优化12.1 使用useMemo避免不必要的计算12.2 使用memo缓存组件,防止过度渲染12.3 useCallBack缓存函数12.4 useCallBack里访问之前的状态(没懂)十三、Styled-Components13.1 安装13.2给普通html元素添加样式13.3 继承和覆盖样式13.4 给react组件添…

Etcd 框架

基本了解 客户端、长连接与租约的关系 客户端对象 etcd的客户端对象是用户与etcd服务进行交互的主要接口&#xff0c;主要功能就是存储、通知和事务等功能访问 键值存储&#xff1a;客户端通过put 和 get操作存储数据&#xff1b;数据存储在etcd的层级化键值数据库中监听器&a…

IDEA2023 创建SpringBoot项目(一)

一、Spring Boot是由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。 二、快速开发 1.打开IDEA选择 File->New->Project 2、…

教育数字化转型新时代:探索智慧学习空间的无限可能

在信息技术的浪潮推动下&#xff0c;教育行业正迎来一场前所未有的变革。这场变革的核心在于教育数字化转型&#xff0c;它要求我们重新审视和构建传统的学习模式&#xff0c;以适应快速变化的社会需求。在这个过程中&#xff0c;智慧学习空间作为数字化转型的重要成果&#xf…

LSTM原理解读与实战

在RNN详解及其实战中&#xff0c;简单讨论了为什么需要RNN这类模型、RNN的具体思路、RNN的简单实现等问题。同时&#xff0c;在文章结尾部分我们提到了RNN存在的梯度消失问题&#xff0c;及之后的一个解决方案&#xff1a;LSTM。因此&#xff0c;本篇文章主要结构如下&#xff…

【成品文章+四小问代码更新】2024亚太杯国际赛B题基于有限差分格式的空调形状优化模型

这里仅展示部分内容&#xff0c;完整内容获取在文末&#xff01; 基于有限差分格式的空调形状优化模型 摘 要 随着科技进步&#xff0c;多功能环境调节设备成为市场趋势&#xff0c;集成了空调、加湿器和空气 净化器功能的三合一设备能提供更舒适健康的室内环境。我们需要分析…

中国省级新质生产力发展指数数据(任宇新版本)2010-2023年

一、测算方式&#xff1a;参考C刊《财经理论与实践》任宇新&#xff08;2024&#xff09;老师的研究&#xff0c;新质生产力以劳动者劳动资料劳动对象及其优化组合的质变为 基本内涵&#xff0c;借 鉴 王 珏 和 王 荣 基 的 做 法构建新质生产力发展水平评价指标体系如下所示&a…

简单理解下基于 Redisson 库的分布式锁机制

目录 简单理解下基于 Redisson 库的分布式锁机制代码流程&#xff1a;方法的调用&#xff1a;具体锁的实现&#xff1a;riderBalance 方法&#xff1a;tryLock 方法&#xff08;重载&#xff09;&#xff1a;tryLock 方法&#xff08;核心实现&#xff09;&#xff1a; 简单理解…