C语言_数据结构总结8:链式队列

纯C语言实现,不涉及C++

链队列

队列的链式表示称为链队列,它实际上是一个同时具有队头指针和队尾指针的单链表,头指针指向对头结点,尾指针指向队尾结点。

头结点

是链式队列中的特殊结点,通常不存储实际的队列元素数据,其主要作用是方便对队列的操作,例如在进行入队、出队操作时,可以统一操作逻辑,无需特殊处理队列为空的情况。它作为队列的头部标识,其next指针指向队列中的第一个真正存储数据的结点

尾结点(注意区分头结点)

在链式队列中,尾结点是最后一个存储有效数据的结点,它的next指针被设置为NULL

链式队列基本操作实现

0. 存储结构声明

cs 复制代码
typedef int ElemType;

typedef struct Linknode {
	ElemType data;  //数据域
	struct Linknode* next;  //指针域
}Linknode;

typedef struct LinkQueue {
	Linknode* front;  //队头指针
	Linknode* rear;  //队尾指针
}LinkQueue;

1. 初始化

cs 复制代码
void InitQueue(LinkQueue* Q) {
	Q->front = Q->rear = (Linknode*)malloc(sizeof(Linknode));
	if (Q->front == NULL)
	{
		printf("内存分配失败!\n");
		return;
	}
	Q->front->next = NULL;
}

2. 判空

cs 复制代码
int QueueEmpty(LinkQueue* Q) {
	return Q->front == Q->rear;
}

3. 入队

cs 复制代码
void enQueue(LinkQueue* Q, ElemType value) {
	//1.申请新结点的内存空间
	Linknode* s = (Linknode*)malloc(sizeof(Linknode));
	//2.检查内存是否分配成功
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return;
	}
	//3.设置新结点的数据和指针
	s->data = value;
	s->next = NULL;
	//4.将新结点连接到队列尾部
	Q->rear->next = s;
	//5.更新队尾指针
	Q->rear = s; //将队尾指针 Q->rear 更新为指向新的队尾结点 s,以便在后续的入队操作或其他与队尾相关的操作中能够正确地访问到队尾。
}

4. 出队

cs 复制代码
int deQueue(LinkQueue* Q) {
	//1. 检查队列是否为空
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可出队!\n");
		return -1;
	}
	//2.保存对头元素的指针
 //创建一个新的指针变量 p,将其指向队头元素的结点(即头结点的下一个结点).因为头结点本身不存储实际的队列元素,所以 Q->front->next 指向的才是真正的队头元素。
	Linknode* p = Q->front->next;  
	
	//3.调整头结点的next指针
 //将头结点的 next 指针指向当前队头元素结点的下一个结点。这样就相当于把原来的队头元素从队列中 "移除" 了,此时头结点的 next 指针指向的结点成为了新的队头元素(如果队列还有其他元素的话)。
	Q->front->next = p->next; 

	//4.处理队列为空的特殊情况
//检查原来的队尾指针 rear 是否指向当前要删除的队头元素结点 p。如果是,说明删除队头元素后队列中没有其他元素了(队列为空),此时需要将 rear 指针重新指向头结点,以保证队列的空状态时 front 和 rear 相等。
	if (Q->rear == p)  
	{
		Q->rear = Q->front;  // 注意是进行赋值,来更新队尾指针,而不是比较==
	}

	//5. 释放队头元素结点的内存
//使用 free 函数释放掉之前保存的队头元素结点(即 p 指向的结点)所占用的内存空间,避免内存泄漏。
	free(p);  

	//6. 返回成功的标志
	return 0;  //出队成功
}

5. 获取队头元素

cs 复制代码
int getQueueValue(LinkQueue* Q, ElemType* value) {
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可出队!\n");
		return -1;
	}
	Linknode* p = Q->front->next;
	*value = p->data;
	return 0;  //查找成功
}

6. 打印

cs 复制代码
void printQueue(LinkQueue* Q) {
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可打印!\n");
		return ;
	}
	Linknode* p = Q->front->next;
	printf("队列中的元素为:");
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

7. 注销

cs 复制代码
void destroyQueue(LinkQueue* Q) {
	while (Q->front != NULL) {
		Q->rear = Q->front->next;
		free(Q->front);
		Q->front = Q->rear;
	}
}

8. 测试

cs 复制代码
int main() {
	LinkQueue Q;
	InitQueue(&Q);

	//入队操作测试
	enQueue(&Q, 11);
	enQueue(&Q, 22);
	enQueue(&Q, 33);
	printQueue(&Q);  //队列中的元素为:11 22 33

	//获取对头元素操作测试
	ElemType value;
	if (getQueueValue(&Q,&value) == 0)
	{
		printf("即将出队的元素是:%d\n", value);  // 即将出队的元素是:11
	}

	//出队操作测试
	if (deQueue(&Q) == 0)
	{
		printf("出队操作成功!\n");  //出队操作成功!
	}
	printQueue(&Q);  //队列中的元素为:22 33

	//判空操作测试
	if (QueueEmpty(&Q))
	{
		printf("队列为空!\n");
	}
	else {
		printf("队列不为空!\n");  //队列不为空!
	}

	//注销队列
	destroyQueue(&Q);
	return 0;
}

9. 完整代码

cs 复制代码
#include<stdio.h>
#include<stdlib.h>

typedef int ElemType;

typedef struct Linknode {
	ElemType data;  //数据域
	struct Linknode* next;  //指针域
}Linknode;

typedef struct LinkQueue {
	Linknode* front;  //队头指针
	Linknode* rear;  //队尾指针
}LinkQueue;

// 操作1------初始化
void InitQueue(LinkQueue* Q) {
	Q->front = Q->rear = (Linknode*)malloc(sizeof(Linknode));
	if (Q->front == NULL)
	{
		printf("内存分配失败!\n");
		return;
	}
	Q->front->next = NULL;
}

// 操作2------判空
int QueueEmpty(LinkQueue* Q) {
	return Q->front == Q->rear;
}

// 操作3------入队
void enQueue(LinkQueue* Q, ElemType value) {
	//1.申请新结点的内存空间
	Linknode* s = (Linknode*)malloc(sizeof(Linknode));

	//2.检查内存是否分配成功
	if (s == NULL)
	{
		printf("内存分配失败!\n");
		return;
	}

	//3.设置新结点的数据和指针
	s->data = value;
	s->next = NULL;

	//4.将新结点连接到队列尾部
	Q->rear->next = s;

	//5.更新队尾指针
//将队尾指针 Q->rear 更新为指向新的队尾结点 s,以便在后续的入队操作或其他与队尾相关的操作中能够正确地访问到队尾。
	Q->rear = s; 
}

// 操作4------出队
int deQueue(LinkQueue* Q) {
	//1. 检查队列是否为空
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可出队!\n");
		return -1;
	}
	//2.保存对头元素的指针
//创建一个新的指针变量 p,将其指向队头元素的结点(即头结点的下一个结点).因为头结点本身不存储实际的队列元素,所以 Q->front->next 指向的才是真正的队头元素。
	Linknode* p = Q->front->next;  
	
	//3.调整头结点的next指针
//将头结点的 next 指针指向当前队头元素结点的下一个结点。这样就相当于把原来的队头元素从队列中 "移除" 了,此时头结点的 next 指针指向的结点成为了新的队头元素(如果队列还有其他元素的话)。
	Q->front->next = p->next;  

	//4.处理队列为空的特殊情况
//检查原来的队尾指针 rear 是否指向当前要删除的队头元素结点 p。如果是,说明删除队头元素后队列中没有其他元素了(队列为空),此时需要将 rear 指针重新指向头结点,以保证队列的空状态时 front 和 rear 相等。
	if (Q->rear == p)  
	{
		Q->rear = Q->front;  // 注意是进行赋值,来更新队尾指针,而不是比较==
	}

	//5. 释放队头元素结点的内存
//使用 free 函数释放掉之前保存的队头元素结点(即 p 指向的结点)所占用的内存空间,避免内存泄漏。
	free(p);  

	//6. 返回成功的标志
	return 0;  //出队成功
}

// 操作5------获取队头元素
int getQueueValue(LinkQueue* Q, ElemType* value) {
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可出队!\n");
		return -1;
	}
	Linknode* p = Q->front->next;
	*value = p->data;
	return 0;  //查找成功
}

// 操作6------打印链式队列
void printQueue(LinkQueue* Q) {
	if (QueueEmpty(Q))
	{
		printf("队列为空,无元素可打印!\n");
		return ;
	}
	Linknode* p = Q->front->next;
	printf("队列中的元素为:");
	while (p != NULL) {
		printf("%d ", p->data);
		p = p->next;
	}
	printf("\n");
}

// 操作7------注销链式队列
void destroyQueue(LinkQueue* Q) {
	while (Q->front != NULL) {
		Q->rear = Q->front->next;
		free(Q->front);
		Q->front = Q->rear;
	}
}

int main() {
	LinkQueue Q;
	InitQueue(&Q);

	//入队操作测试
	enQueue(&Q, 11);
	enQueue(&Q, 22);
	enQueue(&Q, 33);
	printQueue(&Q);  //队列中的元素为:11 22 33

	//获取对头元素操作测试
	ElemType value;
	if (getQueueValue(&Q,&value) == 0)
	{
		printf("即将出队的元素是:%d\n", value);  // 即将出队的元素是:11
	}

	//出队操作测试
	if (deQueue(&Q) == 0)
	{
		printf("出队操作成功!\n");  //出队操作成功!
	}
	printQueue(&Q);  //队列中的元素为:22 33

	//判空操作测试
	if (QueueEmpty(&Q))
	{
		printf("队列为空!\n");
	}
	else {
		printf("队列不为空!\n");  //队列不为空!
	}

	//注销队列
	destroyQueue(&Q);
	return 0;
}

10. 运行截图

分享小妙招:如果对哪个操作不是很明白,就询问AI:请结合以下代码详细描述XXX操作的过程+粘贴的代码

本人菜鸟一只,文章如有错误,欢迎评论区指正!

相关推荐
人工干智能3 小时前
科普:Python 中,字典的“动态创建键”特性
开发语言·python
LGL6030A3 小时前
算法题实战积累(3)——方块转换(C语言)
c语言·算法
初听于你3 小时前
缓存技术揭秘
java·运维·服务器·开发语言·spring·缓存
努力写代码的熊大4 小时前
List迭代器和模拟(迭代器的模拟)
数据结构·windows·list
长路归期无望6 小时前
C语言小白实现多功能计算器的艰难历程
c语言·开发语言·数据结构·笔记·学习·算法
是大强6 小时前
stm32摇杆adc数据分析
开发语言
口嗨农民工6 小时前
win10默认搜索APP和window设置控制命板
linux·服务器·c语言
蓝莓味的口香糖7 小时前
【JS】什么是单例模式
开发语言·javascript·单例模式
linux kernel7 小时前
第二十三讲:特殊类和类型转换
开发语言·c++
笨蛋少年派7 小时前
JAVA基础语法
java·开发语言