数据结构之队列

Posted 芒果再努力

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了数据结构之队列相关的知识,希望对你有一定的参考价值。

前言: 

前面,我们学习了栈,单链表,双向链表,顺序表的内容。大家可以看看我上面写过的文章!

双链表单链表
动态顺序表静态顺序表

目录

一.什么是队列

二.项目创建

三.队列代码实现

1.队列结构体的创建

2.初始化头尾指针

3.队尾入数据

4.队头出数据

5.求队列长度

6.返回队头数据

7.返回队尾数据

8.判断队列是否为空

9.队列销毁

四.总结

五.原码链接


一.什么是队列

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

 

 队尾入数据,队头出数据


问题:选择什么结构实现队列?

 1.若我们使用数组实现队列:插入数据很方便,时间复杂度为O(1),但是删除数据,需要将后面的数据往前覆盖。需要遍历数组效率低,时间复杂度为O(N)

2.若我们使用单链表实现队列:入数据相当于尾插,出数据相当于头删。若我们只定义一个头指针,尾插数据则要遍历找到尾节点,效率低。但是若我们定义两个指针,一个指向头节点,一个指向尾节点。这样的话,入数据和出数据效率都是O(1)。

综上:我们选择单链表实现队列!


二.项目创建

工程文件存放的内容
Queue.h函数声明,宏定义
Queue.c实现顺序表函数接口的定义
test.c测试函数接口是否能实现功能

三.队列代码实现

1.队列结构体的创建

队列:先进先出
只允许一端插入,另一端删除
队尾:入数据   队头:出数据

为了方便后序更改变量,我们用typedef重命名变量。创建结构体队列节点,再创建一个结构体Queue用来存放指向队头的节点和队尾的节点。

typedef int QDataType;
//节点    
typedef struct QueueType
{
	QDataType data;
	struct QueueType* next;
}QNode;

typedef struct Queue
{
	QNode* head;	//指向队头节点
	QNode* tail;	//指向队尾节点
}Queue;

我们定义Queue结构体变量pq 

struct Queue pq;

2.初始化头尾指针

初始化指向队头和队尾的指针为空 。传值时,因为形参是实参的临时拷贝,所以我们要传址。

//初始化头尾指针
void QueueInit(struct Queue* pq)
{
	pq->head = NULL;
	pq->tail = NULL;
}

3.队尾入数据

    动态开辟节点,要判断是否开辟成功!    入数据相当于尾插

注:Queue结构体的头指针(pq->head)指向的就是头节点

 情况1:当队列无数据时,直接将头指针和尾指针指向新开辟的节点

情况2:当队列中有数据时,第一个节点链接新节点。尾指针指向新开的节点

情况1: 

情况2:


//队尾入数据 ---用尾指针尾插
void QueuePush(Queue* pq, QDataType x)
{

	//创建节点
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		printf("malloc fail\\n");
		exit(-1);
	}
	else
	{
		newnode->data = x;
		newnode->next = NULL;
	}
	//队列无数据时
	if (pq->head == NULL)
	{
		pq->head = pq->tail = newnode;
	}
	else
	{
		pq->tail->next = newnode;	//尾节点链接新节点
		pq->tail = newnode;//队尾指针指向新节点
	}
}

4.队头出数据

队头出数据相当于头删

出数据要保证队列中要有数据

情况1:只有一个节点(即pq->head->next == NULL)时,直接释放第一个节点。并且把头指针和尾指针置空!

情况2:有多个节点,保存第二个节点,释放第一个节点,然后头指针指向第二个节点。

//队头出数据---用头指针头删
void QueuePop(Queue* pq)
{
	assert(pq);
	//要保证队列中有数据
	assert(pq->head);
	//当只有一个节点时->free掉第一个节点
	if (pq->head->next == NULL)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	//多个节点---保存第二个节点,释放第一个节点,头指针指向第二个节点
	else
	{
		QNode* next = pq->head->next;//pq->head指向的就是第一个节点 pq->head->next 第二个节点
		free(pq->head);
		pq->head = next;
	}
}

5.求队列长度

1.因为队列长度肯定是大于等于0的,所有函数返回类型可以为size_t

  size_t 即为:unsigned int。  

2.使用计数器,遍历队列求队列长度

//求队列长度
int QueueSize(Queue* pq)
{
	//遍历求长度
	assert(pq);
	int size = 0;
	QNode* cur = pq->head;	//第一个节点
	while (cur)
	{
		size++;
		cur = cur->next;
	}
	return size;
}

6.返回队头数据

头指针指向的就是头节点 

//返回队头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(pq->head);//防止队列为空
	return pq->head->data;
}

7.返回队尾数据

尾指针指向的就是尾节点 

//返回队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(pq->head);//防止队列为空
	return pq->tail->data;
}

8.判断队列是否为空

最初定义头指针为空,如果仍为空指针说明队列为空!

//判断队列是否为空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return  pq->head == NULL;
}

9.队列销毁

节点是动态开辟的,所以我们要销毁空间,防止内存泄漏!遍历队列进行销毁

步骤:释放节点时,要先保留下一个节点,不然就找不到下一个节点!

注意:头指针和尾指针要置空

//销毁
void QueueDestory(Queue* pq)
{
	assert(pq);
	//遍历销毁
	QNode* cur = pq->head;
	while (cur)
	{
		QNode* next = cur->next;//保留下一个节点
		free(cur);
		cur = next;
	}
	pq->head = pq->tail = NULL;
}

四.总结

1.使用单链表实现队列,定义前后指针,一个指向队头,一个指向队尾!这样的话出数据和入数据的效率都是O(1)。  队头出数据,队尾入数据!

  那之前我们实现单链表时为什么不使用双指针呢?-->因为这样我们只能解决尾插,不能解决尾删,还得要找到上一个节点。

2.打印数据时,不能直接打印!不然的话就不满足队列:先进先出的特点。要先取队头数据,再打印,然后出队头数据。以此循环。


五.原码链接

队列源码

以上是关于数据结构之队列的主要内容,如果未能解决你的问题,请参考以下文章

数据结构之链式队列的代码实现及有趣应用

数据结构之链式队列的代码实现及有趣应用

perl中的队列

数据结构之栈和队列

数据结构之队列c代码实现

[数据结构]之队列