C语言设计实现队列

前言

队列是线性表中一种特殊的结构,用来存储和管理数据,并解决一些相关的问题。

提起队列就不得不提起栈了,栈和队列两种结构十分类似,所以在大多数知识体系中都是将栈和队列放在一块对比学习。这一点我是十分认可的,因此我觉得大家在学习队列之余可以参考一下我的上篇文章《C语言设计实现栈》,相信对于你的学习一定会有很好的效果。

本篇文章博主会主要向大家介绍队列的相关概念,以及用C语言设计队列结构,并实现队列的一些基本功能,比如入队列、出队列、返回队头数据等等…

下面开始介绍。


1. 认识队列

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

  • 入队列:进行插入操作的一端称为队尾
  • 出队列:进行删除操作的一端称为队头

在这里插入图片描述

2. 设计队列结构

队列底层可以用数组或者链表的结构实现,相较而言使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

单链表实现队列的示意图如下:
在这里插入图片描述
下面就来定义一个队列的结构:

typedef int QDataType;

// 链式结构:表示队列一个结点
typedef struct QueueNode
{
	struct QueueNode* _next;
	QDataType _data;
}QueueNode;
// 队列的结构
typedef struct Queue
{
	QueueNode* _head; //队头
	QueueNode* _tail;  // 队尾
}Queue;

先定义队列的底层结构单链表结点,链表结点有两个域,一个数据域存放入队列的数据,一个指针域存放指向下一个结点的指针。再定义一个队列结构,队列结构有两个成员,一个是指向队头的指针,一个是指向队尾的指针。

3. 队列相关功能实现

3.1 队列的初始化

实现代码如下:

// 初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->_head = pq->_tail = NULL;
}

队列刚创建之初,先让队列的队头和队尾指向空指针。当然,还要断言接收到的头指针不为空指针。

3.2 数据入队

在用单链表实现队列时,我们规定的是链表的头结点为队头,尾结点处为队尾。根据队列入队的规则,数据要从队尾插入,所以入队的操作实际上就是链表的尾插操作

这里要分两种情况进行:

第一:入队前队内没有数据
此时队列结构内的队头和队尾都指向刚入队的新结点。

第二:入队前队内存在其他数据
这时队头指向的位置不动,将队尾指向的结点和新结点链接在一起,然后队尾指向新结点

实现代码如下:

// 队尾入队列
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));

	if (newnode == NULL)
	{
		printf("内存不足\n");
		exit(-1);
	}

	newnode->_data = x;
	newnode->_next = NULL;

	if (pq->_head == NULL)
	{
		pq->_head = pq->_tail = newnode;
	}

	else
	{
		pq->_tail->_next = newnode;
		pq->_tail = newnode;
	}
}

一开始先动态申请一个新结点,如果申请失败直接结束程序。
新结点申请成功之后将要入队的数据放入新结点中,然后根据我上面提到的两种情况进行入队。

3.3 数据出队

根据队列出队列的规则,数据要从队头删除,所以出队列实际上就是单链表的头删操作

出队列同样要分为两种情况进行:

第一种:出队列前队列中有一个以上的结点
此时只需释放掉队头结点,然后让队头的下一个结点称为新的队头,队尾不用进行任何操作。

第二种:出队列前队列中只有一个结点
此时释放完队头之后,队头指向空指针,同时还需要手动让队尾也指向空指针。

实现代码如下:

// 队头出队列
void QueuePop(Queue* pq)
{
	assert(pq);
	assert(pq->_head);

	QueueNode* next = pq->_head->_next;
	free(pq->_head);
	pq->_head = next;

	if (pq->_head == NULL)
	{
		pq->_tail = NULL;
	}
}

3.4 获取队头数据

获取队头数据只需返回队头指针所指向结点的数据域内容即可,实现代码如下:

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->_head);

	return pq->_head->_data;
}

3.5 获取队尾数据

获取队尾数据只需返回队尾指针所指向结点的数据域内容即可,实现代码如下:

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->_tail);

	return pq->_tail->_data;
}

3.6 检测队列是否为空,如果为空返回非零结果,如果非空返回0

检测队列是否为空只需判断队头指向的结点是否为空,实现代码如下:

int QueueEmpty(Queue* pq)
{
	assert(pq);

	return pq->_head == NULL ? 1 : 0;
}

此处返回一个条件表达式的值即可,如果头结点为空返回1,不为空返回0.

3.7 获取队列中有效元素个数

获取队列有效元素个数,需要将整个队列遍历一遍,有多少个结点就有多少个元素。
实现代码如下:

int QueueSize(Queue* pq)
{
	assert(pq);

	QueueNode* cur = pq->_head;
	int size = 0;

	while (cur)
	{
		++size;
		cur = cur->_next;
	}

	return size;
}

定义一个指针指向头结点,从头结点开始遍历。定义一个整型变量size统计结点个数,遍历一个结点size加1,最终返回size的值即可。

3.8 销毁队列

销毁队列还是需要遍历队列的每一个结点,遍历一个释放一个。等到所有结点都释放掉,再将队头和队尾指针置为空指针。

实现代码如下:

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QueueNode* cur = pq->_head;

	while (cur)
	{
		QueueNode* next = cur->_next;
		free(cur);
		cur = next;
	}

	pq->_head = pq->_tail = NULL;
}

本篇文章到这里就全部结束了,希望可以为大家带来帮助。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值