一、队列的原理
队列和栈相似,相同点是都属于线性结构,不同点是栈的原理是“后进先出”,队列的原理是“先进先出”,队列也被称为”FIFO”。
数据结构中的队列是两端都允许操作,只不过要求数据只能从一端入队(enqueue),从另一端出队(dequeue),一般把入队的一端称为队尾(tail/rear),把出队的一端称为队头/队首(head/front)。
队列也属于线性结构,所以根据数据元素之间的物理关系来划分,同样可以数组或链表为基础来实现队列的操作
1.以数组实现循环队列
如果以数组为基础,一般会把队列设置成循环队列,循环队列也被称为“环形缓冲区”,因为如果队列中的元素出队,则需要把该元素的后继元素整体向前移动,这时时间复杂度为O(n)。

如果数据出队之后不去移动后继元素又会导致数组的空间被浪费,为了解决该问题,可以把队列设置为循环队列,在移动数据的时候只需要移动一次即可,所以时间复杂度就是O(1)

sequencequeue.h
/**
* @file : sequencequeue
* @brief : 使用数组实现循环队列
* @author : TingFengLuo@126.com
* @date : 2026-1-3
* @version : V1.0
* @note :使用循环链表时,要少一个空间,便于分清队首和堆尾
*/
#ifndef __SEQUENCEQUEUE_H_
#define __SEQUENCEQUEUE_H_
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//将int重命名为DataType_t,便于用户后续对类型进行修改
typedef int DataType_t;
//使用数组创建循环队列类型
typedef struct Circular_Sequence_Queue{
//数组容量
int size;
//数组首地址
DataType_t *addr;
//队首下标
int front;
//队尾下标
int rear;
}CirSeqQueue_t;
//创建循环队列并对其元素初始化
CirSeqQueue_t *CirSeqQueue_Create(DataType_t capacity);
//判断循环队列是否满队
bool CirSeqQueue_IsFull(CirSeqQueue_t *queue);
//入队
bool CirSeqQueue_EnQueue(CirSeqQueue_t *queue,DataType_t data);
//判空
bool CirSeqQueue_IsEmpty(CirSeqQueue_t *queue);
//出队
bool CirSeqQueue_DeQueue(CirSeqQueue_t *queue,DataType_t *data);
#endif
sequencequeue.c
#include "sequencequeue.h"
/**
* @name : CirSeqQueue_Create
* @brief : 创建循环队列,并对循环队列初始化
* @param
@size :创建循环队列的元素个数
* @return : 返回循环队列的地址
* @version : V1.0
* @note : NULL
*/
CirSeqQueue_t *CirSeqQueue_Create(DataType_t capacity)
{
//创建变量为循环队列类型申请内存
CirSeqQueue_t *queue = (CirSeqQueue_t *)calloc(1,sizeof(CirSeqQueue_t));
//判断队列内存是否申请成功
if(NULL == queue){
perror("Circular Sequenue Queue create memory failed for queue");
exit(1);
}
//创建变量存储数组容量,addr指向数组首地址
queue->addr = (DataType_t *)calloc(capacity+1,sizeof(DataType_t));
//判断数组是否创建成功
if(NULL == queue->addr){
perror("Circualr Sequence Queue create memory failed for arr");
free(queue);
exit(1);
}
queue->size = capacity+1;
queue->front = 0;
queue->rear = 0;
return queue;
}
/**
* @name : CirSeqQueue_IsFull
* @brief : 判断循环队列是否为满
* @param
@queue : 表示需要操作的循环队列
* @return : 如果循环队列满则返回true,否则返回false
* @version : V1.0
* @note : NULL
*/
bool CirSeqQueue_IsFull(CirSeqQueue_t *queue)
{
return ((queue->rear + 1 ) % queue->size == queue->front) ? true : false;
}
/**
* @name : CirSeqQueue_EnQueue
* @brief : 入队
* @param
@queue : 表示需要操作的循环队列
@data : 表示入队的数据
* @return : 入队成功返回true,否则返回false
* @version : V1.0
* @note : 在入队时,并不是无限插入,所以需要用队尾下标+1,然后和数组元素个数求余
*/
bool CirSeqQueue_EnQueue(CirSeqQueue_t *queue,DataType_t data)
{
//判断循环队列是否存在
if(NULL == queue){
printf("Circular Sequence queue create queue failed\n");
return false;
}
//判断循环队列是否满队
if(CirSeqQueue_IsFull(queue)){
printf("Circular Sequence queue is full\n");
return false;
}
//给数组赋值
queue->addr[queue->rear] = data;
//防止越界
queue->rear = (queue->rear + 1)%queue->size;
return true;
}
/**
* @name : CirSeqQueue_EnQueue
* @brief : 判断循环队列是否为空
* @param
@queue : 表示需要操作的循环队列
* @return : 如果为空返回true,否则返回false
* @version : V1.0
* @note : 判断循环队列是否为空时,只需要判断队首和堆尾下标是否相同就可
*/
bool CirSeqQueue_IsEmpty(CirSeqQueue_t *queue)
{
return queue->rear == queue->front ? true : false;
}
/**
* @name : CirSeqQueue_DeQueue
* @brief : 出队
* @param
@queue : 表示需要操作的循环队列
@data : 表示需要出队的数据
* @return : 如果出队成功返回true,否则返回false
* @version : V1.0
* @note : 出队时要防止出多了,所以要使用队头+1然后对素组元素求余
*/
bool CirSeqQueue_DeQueue(CirSeqQueue_t *queue,DataType_t *data)
{
//判断循环队列是否存在
if(NULL == queue){
printf("Circular Sequence queue create queue failed\n");
return false;
}
//判断循环队列是否为空
if(CirSeqQueue_IsEmpty(queue)){
printf("Circular Sequence queue is Empty\n");
return false;
}
//返回队头的值
*data = queue->addr[queue->front];
//数组中下一个元素的值
queue->front = (queue->front+1) % queue->size;
return true;
}
main.c
#include "sequencequeue.h"
int main(int argc,const char *argv[]){
//创建变量存储循环变量地址
CirSeqQueue_t *queue = CirSeqQueue_Create(20);
CirSeqQueue_EnQueue(queue,10);
CirSeqQueue_EnQueue(queue,20);
CirSeqQueue_EnQueue(queue,30);
CirSeqQueue_EnQueue(queue,40);
CirSeqQueue_EnQueue(queue,50);
CirSeqQueue_EnQueue(queue,60);
CirSeqQueue_EnQueue(queue,70);
int data;
printf("current queue value: ");
while(!CirSeqQueue_IsEmpty(queue)){
CirSeqQueue_DeQueue(queue,&data);
printf("%d ",data);
}
printf("\n");
return 0;
}
代码测试:

二、以链表尾基础实现链式队列
如果打算以链表作为基础实现队列的操作,可以避免内存浪费以及避免内存成片移动,只需要确定队头和队尾即可,一般把链表头部作为队头,可以实现头删,把链表尾部作为队尾,可以实现尾插。

linkedlistqueue.h
/**
* @file : linkedlistqueue
* @brief : 使用链表创建队列,实现链式队列的入队和出队
* @author : TingFengLuo@126.com
* @date : 2026-1-5
* @version : V1.0
* @note : 该链式队列有一个哨兵用来管理,一个节点包含数据域和指针域,有一个头指针和尾指针,便于出队和入队
**/
#ifndef __LINKEDLISTQUEUE_H_
#define __LINKEDLISTQUEUE_H_
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
//将int重命名为Datatype_t,后续便于用于对数据类型进行修改
typedef int DataType_t;
//定义队列的节点结构体
typedef struct node{
//数据域
DataType_t data;
//指针域
struct node *next;
}node_t;
//定义队列管理结构体
typedef struct linkedlistqueue{
//队头指针
node_t *front;
//队尾指针
node_t *rear;
}LLQueue_t;
//创建队列并初始化
LLQueue_t *LLQueue_Create(void);
//判断队列是否为空
bool LLQueue_IsEmpty(LLQueue_t *llqueue);
//创建新节点
node_t *LLQueue_NewNode(DataType_t data);
//入队
bool LLQueue_EnQueue(LLQueue_t *queue,DataType_t data);
//出队
bool LLQueue_DeQueue(LLQueue_t *queue,DataType_t *data);
#endif
linkedlistqueue.c
#include "linkedlistqueue.h"
/**
* @name : LLQueue_Create
* @brief : 创建链式队列,该链式队列包含头节点并对链表初始化
* @param : NULL
* @return : 返回链式队列的地址
* @version : V1.0
* @note : 要对头节点申请内存,也要对队列本身分配内存,让队列的头元素和尾元素指向头节点
*/
LLQueue_t *LLQueue_Create(void)
{
//为队列结构体本身分配内存
LLQueue_t *llqueue = (LLQueue_t *)calloc(1,sizeof(LLQueue_t));
//判断队列是否创建成功
if(NULL == llqueue){
perror("Linked list queue create memory failed for llqueue");
exit(1);
}
//创建变量用于存储哨兵节点
node_t *head = (node_t *)calloc(1,sizeof(node_t));
//判断哨兵节点是否创建成功
if(NULL == head){
perror("Linked list queue create memory failed for head");
free(llqueue);
exit(1);
}
// 初始化哨兵节点
head->next = NULL;
//初始化队列的 front 和 rear 指针,都指向哨兵节点
llqueue->front = head;
llqueue->rear = head;
return llqueue;
}
/**
* @name : LLQueue_IsEmpty
* @brief : 判断队列是否为空
* @param
@llqueue :要操作的队列
* @return : 如果为空返回true,否则返回false
* @version : V1.0
* @note : 判断是否为空只需要判断头节点和尾节点是否相同
*/
bool LLQueue_IsEmpty(LLQueue_t *llqueue)
{
return (llqueue->front->next == NULL) ? true : false;
}
/**
* @name : LLQueue_NewNode
* @brief : 创建新节点,为后续入队左操作
* @param
@data :要入队的数据
* @return : 返回新节点地址
* @version : V1.0
* @note : NULL
*/
node_t *LLQueue_NewNode(DataType_t data)
{
//创建变量存储新节点地址
node_t *new_node = (node_t *)calloc(1,sizeof(node_t));
//判断新节点是否创建成功
if(NULL == new_node){
printf("create new node failed\n");
return NULL;
}
new_node->data = data;
new_node->next = NULL;
return new_node;
}
/**
* @name : LLQueue_EnQueue
* @brief : 入队
* @param
@queue :要操作的队列
@data :要入队的数据
* @return : 入队成功返回true否则返回false
* @version : V1.0
* @note : NULL
*/
bool LLQueue_EnQueue(LLQueue_t *queue,DataType_t data)
{
//判断队列是否存在
if(NULL == queue){
printf("create memory failed for queue\n");
return false;
}
//创建变量存储新节点地址
node_t *new_node = LLQueue_NewNode(data);
if(NULL == new_node){
printf("create new_node failed\n");
return false;
}
//判断队列是否为空,如果为空,直接插入新节点
if(queue->front->next == NULL){
queue->rear->next = new_node;
queue->rear = queue->rear->next;
return true;
}
queue->rear->next = new_node;
queue->rear = queue->rear->next;
return true;
}
/**
* @name : LLQueue_DeQueue
* @brief : 出队
* @param
@queue :要操作的队列
@data :返回出队的数据
* @return : 出队成功返回true否则返回false
* @version : V1.0
* @note : NULL
*/
bool LLQueue_DeQueue(LLQueue_t *queue,DataType_t *data)
{
//判断链式队列是否存在
if(NULL == queue){
printf("create queue failed\n");
return false;
}
//判断队列是否为空
if(queue->front->next == NULL){
printf("queue is empty\n");
return false;
}
//创建变量用于存储要出队的节点
node_t *pnode = queue->front->next;
//将头节点的下一个指针指向出队的下一个节点
queue->front->next = pnode->next;
//如果是最后一个节点
if(pnode == queue->rear){
queue->front->next = NULL;
queue->rear = queue->front;
}
*data = pnode->data;
free(pnode);
pnode = NULL;
return true;
}
main.c
#include "linkedlistqueue.h"
int main(int argc,const char *argv[]){
LLQueue_t * queue = LLQueue_Create();
if(NULL == queue){
printf("create queue failed!!!\n");
return -1;
}
//创建变量用于存储返回的数据
DataType_t data;
LLQueue_EnQueue(queue,10);
LLQueue_EnQueue(queue,20);
LLQueue_EnQueue(queue,30);
LLQueue_EnQueue(queue,40);
LLQueue_EnQueue(queue,50);
LLQueue_EnQueue(queue,60);
LLQueue_EnQueue(queue,70);
printf("Dequeue value : ");
while(!LLQueue_IsEmpty(queue)){
LLQueue_DeQueue(queue,&data);
printf("%d ",data);
}
printf("\n");
free(queue);
queue = NULL;
return 0;
}
代码测试:
















发表回复