2026/7/26 数据结构7(队列)

一、队列的原理与应用

队列(Queue)和栈类似,相同点是都属于线性结构,不同点是栈遵循“后进先出”原则,而队列遵循“先进先出”的原则,也被成为“FIFO”结构,就是“First Input First Output”

数据结构中的队列的两端都允许操作,只不过要求数据只能从队列的一端插入,从队列的另一端删除,可以把队列理解为一根水管,水管有进水口和出水口。一般把允许数据插入的一端称为队尾(Tail或者Rear),一般把允许删除数据的一端称为队头队首(Head或者Front)。

队列也属于线性结构,所以根据数据元素之间的物理关系来划分的话同样可以以数组或者链表为基础来实现队列的操作。

1.以数组为基础实现循环队列

如果以数组为基础,如果队列中的元素出队,则需要把该元素的后继元素整体向前移动,这是时间复杂度为O(n)的操作。
一般会把队列设置为循环队列,循环队列也被称为“环形缓冲区” ,在移动数据的时候只需要移一次即可,所以时间复杂度就是O(1)。

编写如下C语言代码实现循环队列的构造,入队,出队并验证

/********************************************************************************************************
* @File Name:SequenceQueue.c
* @Author   :bbzhu04@gmail.com
* @Date     :2026/07/27
* @Function :构造函数实现循环队列的入队出队等操作
* @Notes     :为了提高可移植性,所以循环队列中元素的数据类型为DataType_t,用户可以根据实际情况修改循环队列中元素的类型
*			另外,为了方便管理循环队列,设计CirQueue_t结构体,该结构体中包含四个成员:地址+容量+队尾下标+队首下标
* Copyright (c)  2025-2026   bbzhu04@gmail.com   All right Reserved
* ******************************************************************************************************/


#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>



//指的是循环队列中的元素的数据类型,用户可以根据需要进行修改
typedef int  DataType_t;

//构造记录循环队列CircularQueue各项参数(循环队列的首地址 + 循环队列的容量 + 循环队列队尾下标+队首下标)的结构体
typedef struct CircularQueue
{
	DataType_t * Addr;		//记录循环队列首地址
	unsigned int Size;		//记录循环队列的容量
	int			 Rear;      //循环队列队尾下标	
	int			 Front;     //循环队列队首下标

}CirQueue_t;


/********************************************************************************************************
 * @Function Name		:CirQueue_Create
 * @Functionality		:创建循环队列并对循环队列进行初始化
 * @Function Parameters	:None
 * @Return result		:指向循环队列管理结构体的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
CirQueue_t * CirQueue_Create(unsigned int size)
{
	//1.利用calloc为循环队列的管理结构体申请一块堆内存
	CirQueue_t *Manager = (CirQueue_t *)calloc(1,sizeof(CirQueue_t));

	if(NULL == Manager)
	{
		perror("calloc memory for manager is failed");
		exit(-1); //程序异常终止
	}

	//2.利用calloc为所有元素申请堆内存
	Manager->Addr = (DataType_t *)calloc(size,sizeof(DataType_t));

	if (NULL == Manager->Addr)
	{
		perror("calloc memory for element is failed");
		free(Manager);
		exit(-1); //程序异常终止
	}

	//3.对管理循环队列的结构体进行初始化(循环队列容量 + 队尾下标+队首下标)
	Manager->Size = size;	//对循环队列中的容量进行初始化
	Manager->Rear = 0;		//队尾下标初值为0
	Manager->Front = 0;		//队首下标初值为0
	
	return Manager;
}


/********************************************************************************************************
 * @Function Name		:CirQueue_IsFull
 * @Functionality		:判断循环队列是否已满
 * @Function Parameters	:
 * 							@Manager:指向循环队列管理结构体的指针
 * @Return result		:bool型变量,循环队列已满时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool CirQueue_IsFull(CirQueue_t *Manager)
{
	return ( (Manager->Rear + 1) % Manager->Size == Manager->Front ) ? true : false;
}


/********************************************************************************************************
 * @Function Name		:CirQueue_IsEmpty
 * @Functionality		:判断循环队列是否为空
 * @Function Parameters	:
 * 							@Manager:指向循环队列管理结构体的指针
 * @Return result		:bool型变量,循环队列为空时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool CirQueue_IsEmpty(CirQueue_t *Manager)
{
	return (Manager->Front == Manager->Rear) ? true : false;
}



/********************************************************************************************************
 * @Function Name		:CirQueue_Enqueue
 * @Functionality		:入队操作
 * @Function Parameters	:
 * 							@Manager:指向循环队列管理结构体的指针
 * 							@Data   :要入队的元素值
 * @Return result		:bool型变量,入队成功时返回true,失败返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool CirQueue_Enqueue(CirQueue_t *Manager, DataType_t Data)
{
	//1.判断循环队列是否已满
	if ( CirQueue_IsFull(Manager) )
	{
		printf("CirQueue is Full!\n");
		return false;
	}

	//2.如果循环队列有空闲空间,则把新元素添加到循环队列尾部
	Manager->Addr[Manager->Rear] = Data;


	//防止队尾下标越界
	Manager->Rear = (Manager->Rear+1) % Manager->Size;


	return true;
}


/********************************************************************************************************
 * @Function Name		:CirQueue_Dequeue
 * @Functionality		:出队操作
 * @Function Parameters	:
 * 							@Manager:指向循环队列管理结构体的指针
 * 							@Data   :要出队的元素值
 * @Return result		:bool型变量,出队成功时返回true,失败返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
DataType_t CirQueue_Dequeue(CirQueue_t *Manager)
{
	DataType_t temp =0;

	//1.判断循环队列是否为空
	if ( CirQueue_IsEmpty(Manager) )
	{
		printf("CirQueue is Empty!\n");
		return false;
	}

	//2.把元素从队首出队,并备份到变量temp
	temp = Manager->Addr[Manager->Front];
	
	//防止队首下标越界
	Manager->Front = (Manager->Front+1) % Manager->Size;


	return temp;
}


/********************************************************************************************************
 * @Function Name		:SeqQueue_Print
 * @Functionality		:遍历循环队列的元素
 * @Function Parameters	:
 * 							@Manager   :指向循环队列管理结构体的指针
 * @Return result		:None
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
void SeqQueue_Print(CirQueue_t *Manager)
{
	for (int i = Manager->Front; i != Manager->Rear; i = (i + 1) % Manager->Size)  //遍历循环队列中的元素
	{
		printf(" Queue Element[%d] = %d\n",i,Manager->Addr[i]);
	}
}


int main(int argc, char const *argv[])
{

	//创建循环队列
	CirQueue_t *Manager = CirQueue_Create(5);

	//入队操作
	CirQueue_Enqueue(Manager,1);
	CirQueue_Enqueue(Manager,2);
    //遍历输出循环队列的元素
	SeqQueue_Print(Manager);
	printf("\n");


	//出队操作
	DataType_t dequeuedElement = CirQueue_Dequeue(Manager);
	printf("Dequeued Element: %d\n", dequeuedElement);
	//遍历输出循环队列的元素
	SeqQueue_Print(Manager);


	
	return 0;
}


运行结果:

image

2.以链表为基础实现链式队列

如果打算以链表作为基础来实现队列的操作,可以避免内存浪费以及避免内存成片移动,只需要确定队头和队尾即可,一般把链表头部作为队头,可以实现头删,把链表尾部作为队尾,可以实现尾插。
以单向链表为例
可以表头入队,表尾出队,也可以表尾入队,表头出队
分别编写如下C语言代码实现两种方案链式队列的构造,入队,出队并验证

(1)表头入队,表尾出队

/********************************************************************************************************
 * @File Name		:LinkedQueue 1.c
 * @Author			:bbzhu04@gmail.com
 * @Date			:2026/07/26
 * @Function		:构造函数实现链式队列(表头入队,表尾出队)元素的入队,出队等操作
 * @Notes			:为了提高可移植性,所以链表中元素的数据类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
 * 				 	 另外,为了方便管理链表,所以用户设计LQueue_t结构体,该结构体中包含两个成员:结点的数据域和指针域
* Copyright (c)  2025-2026   bbzhu04@gmail.com   All right Reserved
* ******************************************************************************************************/

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

//指的是单向链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int  DataType_t;

//构造链表的结点,链表中所有结点的数据类型应该是相同的(与顺序表不同,链表中每个结点都是一个独立的结构体,而顺序表中所有元素都是一个连续的内存空间)
typedef struct LinkedQueue
{
	DataType_t  		 data; //结点的数据域
	struct LinkedQueue	*next; //结点的指针域

}LQueue_t;


/********************************************************************************************************
 * @Function Name		:LQueue_Create
 * @Functionality		:创建链式队列并对队列进行初始化
 * @Function Parameters	:None
 * @Return result		:指向链式队列管理结构体的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
LQueue_t * LQueue_Create(void)
{
	//1.创建一个头结点并对头结点申请内存
	LQueue_t *Head = (LQueue_t *)calloc(1,sizeof(LQueue_t));
	if (NULL == Head) 
	{
		perror("Calloc memory for Head is Failed");
		exit(-1);
	}

	//2.对头结点进行初始化,头结点是不存储有效内容的!!!
	Head->next = NULL;

	//3.把头结点的地址返回即可
	return Head;
}

/********************************************************************************************************
 * @Function Name		:LQueue_NewNode
 * @Functionality		:创建新的结点,并对新结点进行初始化(数据域 + 指针域)
 * @Function Parameters	:
 *                          @data:要插入到新结点中的数据
 * @Return result		:指向链式队列新结点的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
LQueue_t * LQueue_NewNode(DataType_t data)
{
	//1.创建一个新结点并对新结点申请内存
	LQueue_t *New = (LQueue_t *)calloc(1,sizeof(LQueue_t));
	if (NULL == New)
	{
		perror("Calloc memory for NewNode is Failed");
		return NULL;
	}

	//2.对新结点的数据域和指针域进行初始化
	New->data = data;
	New->next = NULL;

	return New;
}

/********************************************************************************************************
 * @Function Name		:LQueue_Enqueue
 * @Functionality		:入队
 * @Function Parameters	:
 *                          @Head:指向链式队列的指针
 *                          @data:要插入到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool LQueue_Enqueue(LQueue_t *Head,DataType_t data)
{
	//1.创建新的结点,并对新结点进行初始化
	LQueue_t *New = LQueue_NewNode(data);
	if (NULL == New)
	{
		printf("can not insert new node\n");
		return false;
	}

	//2.判断链表是否为空,如果为空,则直接插入即可
	if (NULL == Head->next)
	{
		Head->next = New;
		return true;
	}

	//3.如果链表为非空,则把新结点插入到链表的头部
	New->next  = Head->next;
	Head->next = New;

	return true;
}


/********************************************************************************************************
 * @Function Name		:LQueue_Dequeue
 * @Functionality		:出队
 * @Function Parameters	:
 *                          @Head:指向链式队列的指针
 * @Return result		:bool型变量,出队成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
DataType_t LQueue_Dequeue(LQueue_t *Head)
{
	//1.判断链表是否为空,如果为空,则无法出队
	if (NULL == Head->next)
	{
		printf("Queue is empty\n");
		return 0;
	}

	//2.删除链表的尾结点
	LQueue_t *Phead = Head;
	while (Phead->next->next!= NULL)
	{
		Phead = Phead->next;
	}
	DataType_t data = Phead->next->data;
	free(Phead->next);
	Phead->next = NULL;
	return data;
}


/********************************************************************************************************
 * @Function Name		:LQueue_Print
 * @Functionality		:遍历输出链式队列的元素
 * @Function Parameters	:
 *                          @Head:指向链式队列头结点的指针
 * @Return result		:void
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
void LQueue_Print(LQueue_t *Head)
{
	//对链式队列的头结点的地址进行备份
	LQueue_t *Phead = Head;
	
	while(Phead->next)//指针域不为空,则说明当前结点不是尾结点
	{
		//把头的直接后继作为新的头结点
		Phead = Phead->next;

		//输出头结点的直接后继的数据域
		printf("data = %d\n",Phead->data);
	}

}




int main(int argc, char const *argv[])
{

	//1.创建链式队列
	LQueue_t *Queue = LQueue_Create();

	//2.入队操作
	LQueue_Enqueue(Queue,1);
	LQueue_Enqueue(Queue,2);
	LQueue_Enqueue(Queue,3);
	LQueue_Print(Queue); //遍历输出链式队列的元素
	printf("\n");  //3 2 1

	//3.出队操作
	DataType_t data = LQueue_Dequeue(Queue);
	printf("Dequeued data: %d\n", data);	
	LQueue_Print(Queue); //遍历输出链式队列的元素
	printf("\n");  //3 2 

	return 0;
}

运行结果:

image

(2)表尾入队,表头出队

/********************************************************************************************************
 * @File Name		:LinkedQueue 2.c
 * @Author			:bbzhu04@gmail.com
 * @Date			:2026/07/26
 * @Function		:构造函数实现链式队列(表尾入队,表头出队)元素的入队,出队等操作
 * @Notes			:为了提高可移植性,所以链表中元素的数据类型为DataType_t,用户可以根据实际情况修改链表中元素的类型
 * 				 	 另外,为了方便管理链表,所以用户设计LQueue_t结构体,该结构体中包含两个成员:结点的数据域和指针域
* Copyright (c)  2025-2026   bbzhu04@gmail.com   All right Reserved
* ******************************************************************************************************/

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>

//指的是单向链表中的结点有效数据类型,用户可以根据需要进行修改
typedef int  DataType_t;

//构造链表的结点,链表中所有结点的数据类型应该是相同的(与顺序表不同,链表中每个结点都是一个独立的结构体,而顺序表中所有元素都是一个连续的内存空间)
typedef struct LinkedQueue
{
	DataType_t  		 data; //结点的数据域
	struct LinkedQueue	*next; //结点的指针域

}LQueue_t;


/********************************************************************************************************
 * @Function Name		:LQueue_Create
 * @Functionality		:创建链式队列并对队列进行初始化
 * @Function Parameters	:None
 * @Return result		:指向链式队列管理结构体的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
LQueue_t * LQueue_Create(void)
{
	//1.创建一个头结点并对头结点申请内存
	LQueue_t *Head = (LQueue_t *)calloc(1,sizeof(LQueue_t));
	if (NULL == Head) 
	{
		perror("Calloc memory for Head is Failed");
		exit(-1);
	}

	//2.对头结点进行初始化,头结点是不存储有效内容的!!!
	Head->next = NULL;

	//3.把头结点的地址返回即可
	return Head;
}

/********************************************************************************************************
 * @Function Name		:LQueue_NewNode
 * @Functionality		:创建新的结点,并对新结点进行初始化(数据域 + 指针域)
 * @Function Parameters	:
 *                          @data:要插入到新结点中的数据
 * @Return result		:指向链式队列新结点的指针
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
LQueue_t * LQueue_NewNode(DataType_t data)
{
	//1.创建一个新结点并对新结点申请内存
	LQueue_t *New = (LQueue_t *)calloc(1,sizeof(LQueue_t));
	if (NULL == New)
	{
		perror("Calloc memory for NewNode is Failed");
		return NULL;
	}

	//2.对新结点的数据域和指针域进行初始化
	New->data = data;
	New->next = NULL;

	return New;
}


/********************************************************************************************************
 * @Function Name		:LQueue_Enqueue
 * @Functionality		:入队
 * @Function Parameters	:
 *                          @Head:指向链式队列的指针
 *                          @data:要插入到新结点中的数据
 * @Return result		:bool型变量,插入成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
bool LQueue_Enqueue(LQueue_t *Head,DataType_t data)
{
    //1.创建新的结点,并对新结点进行初始化
    LQueue_t *New = LQueue_NewNode(data);
    if (NULL == New)
    {
        printf("can not insert new node\n");
        return false;
    }

    //2.判断链表是否为空,如果为空,则直接插入即可
    if (NULL == Head->next)
    {
        Head->next = New;
        return true;
    }

    //3.如果链表为非空,则把新结点插入到链表的尾部
    LQueue_t *Phead = Head;
    while (Phead->next)//指针域不为空,则说明当前结点不是尾结点
    {
        Phead = Phead->next;
    }
    Phead->next = New;

    return true;
}


/********************************************************************************************************
 * @Function Name		:LQueue_Dequeue
 * @Functionality		:出队
 * @Function Parameters	:
 *                          @Head:指向链式队列的指针
 * @Return result		:bool型变量,出队成功时返回true,否则返回false
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/
DataType_t LQueue_Dequeue(LQueue_t *Head)
{
	//1.判断链表是否为空,如果为空,则直接返回NULL
    if (NULL == Head->next)
    {
        printf("LQueue is Empty!\n");
        return 0;
    }

    //2.删除链表的头结点
    LQueue_t *Phead = Head->next;
    DataType_t data = Phead->data;
    Head->next = Phead->next;
    free(Phead);
    return data;
}


/********************************************************************************************************
 * @Function Name		:LQueue_Print
 * @Functionality		:遍历输出链式队列的元素
 * @Function Parameters	:
 *                          @Head:指向链式队列头结点的指针
 * @Return result		:void
 * @Notes				:None
 * @Author				:bbzhu04@gmail.com
 * @Revision History		:None
 * @Function version		:V1.0
 * *****************************************************************************************************/  
void LQueue_Print(LQueue_t *Head)
{
	//对链式队列的头结点的地址进行备份
	LQueue_t *Phead = Head;
	
	while(Phead->next)//指针域不为空,则说明当前结点不是尾结点
	{
		//把头的直接后继作为新的头结点
		Phead = Phead->next;

		//输出头结点的直接后继的数据域
		printf("data = %d\n",Phead->data);
	}

}




int main(int argc, char const *argv[])
{

	//1.创建链式队列
	LQueue_t *Queue = LQueue_Create();

	//2.入队操作
	LQueue_Enqueue(Queue,1);
	LQueue_Enqueue(Queue,2);
	LQueue_Enqueue(Queue,3);
	LQueue_Print(Queue); //遍历输出链式队列的元素
	printf("\n");  //1 2 3

	//3.出队操作
	DataType_t data = LQueue_Dequeue(Queue);
	printf("Dequeued data: %d\n", data);	
	LQueue_Print(Queue); //遍历输出链式队列的元素
	printf("\n");  //2 3

	return 0;
}

运行结果:

image

posted @ 2026-07-26 22:52  !城北徐公.!  阅读(10)  评论(0)    收藏  举报