c语言实现队列


在学习线性表后,可以把队列看成一种特殊的线性表,只是这种线性表只支持尾插与头删。满足数据先进先出的特点

例如把1,2,3这三个数据依次插入队列,此时的队列1为队头,3为队尾
在这里插入图片描述
若将把4入队,1为队头,4为队尾(相当于尾插)
在这里插入图片描述
若将数据出队,由于队列只能头删,所以1出队;2为对头,4为队尾
在这里插入图片描述
下面是实现这样结构的代码

链式结构实现队列

根据在内存上存储空间的连续性,实现队列的结构分为链表和顺序表。顺序表的头删要挪动数据,时间开销大,考虑时间的开销,链表是一种更适合实现队列的结构。

链表要实现尾插,可以创建一个尾指针指向尾节点,避免遍历链表花费多余时间。所以一个队列的链式结构中,要有一个头指针与一个尾指针,分别指向头节点与尾节点。

队列中的节点要包含一个数据域和一个指针域。

结构声明

typedef int QDataType;//队列中数据的类型为QDataType
typedef struct QueueNode
{
	QDataType data;//数据域
	struct QueueNode* next;//指针域
}QueueNode;//为书写方便,将struct QueueNode重新命名为QueueNode

//声明一个包含两个指针队列
typedef struct Queue
{
	QueueNode* head;
	QueueNode* tail;
}Queue;

队列的主要接口

队列的常用接口如下

void QueueInit(Queue* pq);//创建一个队列时,将其初始化
void QueueDestory(Queue* pq);//当不使用队列时,将其销毁
void QueuePush(Queue* pq, QDataType x);//将数据为x的节点入队
void QueuePop(Queue* pq);//队列的出队
bool QueueEmpty(Queue* pq);//队列的判空,空返回true,非空返回false
int QueueSize(Queue* pq);//求队列长度
QDataType QueueFront(Queue* pq);//返回队头元素
QDataType QueueTail(Queue* pq);//返回队尾元素

队列初始化与销毁

//初始化,将队列的头尾指针置空
void QueueInit(Queue* pq)
{
	assert(pq);//断言pq不为空指针,若指向队列的指针为空,则找不到该队列,不能更改该队列内容
	pq->head = NULL;
	pq->tail = NULL;
}
void QueueDestory(Queue* pq);
{
	assert(pq);
	
	QueueNode* curr = pq->head;
	while (curr)
	{
		QueueNode* next = curr->next;
		free(curr);
		curr = next;
	}

	pq->head = NULL;
	pq->tail = NULL;
}	

队列的入队与出队

队列为空时,调用初始化函数后,head与tail都指向空。所以队列为空时,插入节点后要将head与tail都指向该节点。

在这里插入图片描述
在这里插入图片描述
若再插入数据,此时队列不为空,插入数据就要将尾节点的指针指向要插入的节点。
在这里插入图片描述
再将tail指向要插入的节点
在这里插入图片描述
所以入队分为两种情况

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	//先创建新的节点
	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
	newnode->next = NULL;
	newnode->data = x;
	
	//若尾指针为空,则队列为空,插入节点时,将head与tail都指向该节点
	if (pq->tail == NULL)
	{
		pq->tail = newnode;
		pq->head = newnode;
	}
	else//队列不为空
	{
		pq->tail->next = newnode;//通过尾指针找到尾节点,将尾节点的next指向新节点
		pq->tail = newnode;//再将尾指针指向新节点
	}
}

出队时释放队列中的头节点,并且将头指针指向头节点的下一个节点
在这里插入图片描述
在这里插入图片描述
但当队列中只有一个节点时,这样删除元素就会出现问题
在这里插入图片描述
first是指向5这个节点的指针,second则指向空,释放first之后,head指向second,也就是空,但tail指针依然指向5这个节点,其内存空间已经还给了操作系统,tail成为野指针

(细节点:p指向一块程序向操作系统申请的内存空间,大小为4字节,free函数释放空间,是将p指向的空间上的数据重置,并没有改变p的值,所以这是p是一个野指针,在free函数释放指针所指向空间后,通常要带上把指针指空的操作,防止野指针的出现。)

所以要加上一个判断:当head为空时,tail也要置为空
在这里插入图片描述

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->head)//若队列为空不能删除
	
	QueueNode* first = pq->head;//保存头节点与下一个节点
	QueueNode* second = first->next;

	free(first);//free掉first之后将head指向second
	pq->head = second;	
	
	if (pq->head == NULL)
	{
		pq->tail = NULL;
	}
}

队列判空与求队列长度

判空操作:判断头指针是否为空;

求长度:创建curr指针,count计数,用curr去遍历整个队列,遇到节点count加1,直到curr为空

这两个接口较简单,坑点少,直接贴代码

//判空的代码不用if语句写,pq->head == NULL,这个表达式为真,表达式结果为非0,表达式结果为假,结果为0
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return (pq->head == NULL);
}

int QueueSize(Queue* pq)
{
	assert(pq);
	
	QueueNode* curr = pq->head;//curr刚开始时指向队列的头
	int count = 0;
	while (curr)//用curr遍历队列
	{
		count++;
		curr = curr->next;
	}
	
	return count;
}	

取队列头节点与尾节点的数据

由于头尾指针的存在,通过头尾指针,能很快完成取头或取尾的操作

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);//队列不能为空
	return (pq->head->data);
}

QDataType QueueTail(Queue* pq)
{
	assert(pq);
	assert(pq->head);//队列不能为空
	return (pq->tail->data);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值