【C语言】队列的实现(数据结构)

前言:

相信大家在生活中经常排队买东西,今天学习的队列就跟排队买东西一样,先来买的人就买完先走,也就是先进先出。废话不多说,进入咱们今天的学习吧。

目录

前言:

队列的概念

队列的实现

队列的定义

入队列

判空

出队列

队列初始化

获取队头数据

获取队尾数据

获取有效数量

队列销毁

队列详细代码

头文件Queue.h

函数文件Queue.c

测试Text.cjt


队列的概念

想要去实现队列,首先要了解队列的结构和内容。

队列有队头个队尾,出队列只能在队头出(头删),入队列只能在队尾入(尾插)。

  队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先
进先出FIFO(First In First Out)。
入队列:进行插入操作的一端称为队尾。
出队列:进行删除操作的一 端称为队头。

队列的实现

前面我们学习了单链表的基本实现,如果掌握了单链表,那么队列的实现就轻而易举了。

因为队列的本质是先进先出,也就是和头节点删除一样,如果我们利用顺序表的结构来实现队列,删掉一个头节点,后面的数据都要往前移动一位,时间复杂度为O(N),所以我们利用单链表来实现队列,既方便又简洁。

队列的定义

我们既然利用单链表来实现队列,那么一个节点里面就会包含一个指向下一个节点的指针和我们要存放的数据。

typedef int QDataType;
//表示队列节点的定义
typedef struct QListNode
{
	struct QListNode* next;
	QDataType val;
}QNode;

这里将int 定义为 QDataType 方便数据类型的更改。

在实现单链表的时候我们只需要了一个头指针,实现队列的时候我们需要加上一个尾指针,这样方便我们找到头尾数据,同时还可以避免遍历找尾,提高的算法的效率。

更重要的是,在的单链表实现中我们的头删,头插都用到了二级指针,考虑到链表为空,只有二级指针才能更好的方便改变指向节点指针的值,但队列我们增加的一个尾指针的话,改变头节点里面的内容就不需要传二级指针了。

//队列的结构
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

后面我们要计算队列中有效数据的元素个数,所以我们加一个size进去。

入队列

数据插入队列的时候,我们要考虑队列为空的情况,最后要注意size要++!!!

//队尾入队列
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)//考虑newnode为空的情况
	{
		printf("malloc fail!!!");
		exit(1);
	}
	newnode->next = NULL;
	newnode->val = x;

	if (QueueEmpty(q))//队列为空
	{
		q->head = q->tail = newnode;
	}
	else {           //队列不为空
		q->tail->next = newnode;
		q->tail = newnode;
	}
	q->size++;//插入数据之后不要忘了把size++
}

判空

如果为空则返回非零结果,如果非空则返回0

int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}

出队列

这里也要注意,如果只有一个节点的时候,q->head = NULL的同时要保证q->tail也要为空,不然就会出现野指针的情况!

//队头出队列
void QueuePop(Queue* q)
{
	assert(q);
    assert(q->head);
	if (q->head->next == NULL)
	{
		free(q->head);
		q->head = q->tail = NULL;
	}
	else {
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->size--;
}

一定要写assert(q->head);这样就保证下面的q->head->next没有问题,这样写是为了防止空指针被解引用!

队列初始化

void QueueInit(Queue* q)
{
	assert(q);
	q->head = q->tail = NULL;
	q->size = 0;
}

获取队头数据

QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->head);
	return q->head->val;
}

获取队尾数据

QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->tail);
	return q->tail->val;
}

这是也要注意要对q->head和q->tail进行断言。

获取有效数量

int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}

队列销毁

void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* pcur = q->head;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(q->head);
		pcur = next;
		
	}
	q->head = q->tail = NULL;
	q->size = 0;
}

在while循环里面要注意将pcur的下一个节点用新指针保存起来,这样更方便我们去释放空间。

队列详细代码

头文件Queue.h

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

typedef int QDataType;
//表示队列节点的定义
typedef struct QListNode
{
	struct QListNode* next;
	QDataType val;
}QNode;
//队列的结构
typedef struct Queue
{
	QNode* head;
	QNode* tail;
	int size;
}Queue;

//初始化队列
void QueueInit(Queue* q);
//队尾入队列
void QueuePush(Queue* q,QDataType x);
//队头出队列
void QueuePop(Queue* q);
//获取队列头部元素
QDataType QueueFront(Queue* q);
//获取队列尾部元素
QDataType QueueBack(Queue* q);
//获取队列中有效元素个数
int QueueSize(Queue* q);
//检验队列是否为空,如果为空则返回非零结果,如果非空则返回0
int QueueEmpty(Queue* q);
//销毁队列
void QueueDestroy(Queue* q);

函数文件Queue.c

#include"Queue.h"
//初始化队列
void QueueInit(Queue* q)
{
	assert(q);
	q->head = q->tail = NULL;
	q->size = 0;
}
//队尾入队列
void QueuePush(Queue* q, QDataType x)
{
	assert(q);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)//考虑newnode为空的情况
	{
		printf("malloc fail!!!");
		exit(1);
	}
	newnode->next = NULL;
	newnode->val = x;

	if (QueueEmpty(q))//队列为空
	{
		q->head = q->tail = newnode;
	}
	else {           //队列不为空
		q->tail->next = newnode;
		q->tail = newnode;
	}
	q->size++;//插入数据之后不要忘了把size++
}

//队头出队列
void QueuePop(Queue* q)
{
	assert(q);
	if (q->head->next == NULL)
	{
		free(q->head);
		q->head = q->tail = NULL;
	}
	else {
		QNode* next = q->head->next;
		free(q->head);
		q->head = next;
	}
	q->size--;
}
//获取队列头部元素
QDataType QueueFront(Queue* q)
{
	assert(q);
	assert(q->head);
	return q->head->val;
}
//获取队列尾部元素
QDataType QueueBack(Queue* q)
{
	assert(q);
	assert(q->tail);
	return q->tail->val;
}
//获取队列中有效元素个数
int QueueSize(Queue* q)
{
	assert(q);
	return q->size;
}
//检验队列是否为空,如果为空则返回非零结果,如果非空则返回0
int QueueEmpty(Queue* q)
{
	assert(q);
	return q->size == 0;
}
//销毁队列
void QueueDestroy(Queue* q)
{
	assert(q);
	QNode* pcur = q->head;
	while (pcur)
	{
		QNode* next = pcur->next;
		free(q->head);
		pcur = next;
		
	}
	q->head = q->tail = NULL;
	q->size = 0;
}

测试Text.cjt

#include"Stack.h"
#include"Queue.h"
void QueueText()
{
	Queue q;
	QueueInit(&q);//传地址过去才能影响结构体里面的值
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	while (!QueueEmpty(&q))
	{
		printf("%d", QueueFront(&q));
		QueuePop(&q);
		printf("\n");
	}
	QueueDestroy(&q);
}
int main()
{
	QueueText();
	return 0;
}


今天的分享就到这啦!如果大家有所感悟或者见解多多分享哈。

记得三连哦,有不好的地方欢迎大佬指正!

  • 24
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值