数据结构:队列

目录

 一、队列的结构与概念

二、队列的实现

2.1、队列的一些主要功能

2.2.1 入队列

2.2.2 出队列

2.2.3 队列判空 

2.2.4 取队头数据和取队尾数据

2.2.5 判断队列中的数据个数

2.2.6 队列销毁

总结 


 一、队列的结构与概念

概念:队列只允许在一端进行插入数据操作,在另一端进行删除数据的特殊线性表,队列具有先进先出。其中入队列的一端我们称为队尾,出队列的一端我们称为队头。

那么从图中我们便可以大致看出,数据是从队尾进入的,出数据是从队头出的。

在队列底层结构类型的选择上,我们主要选择两种,一种是数组,另一种是链表。本篇博客主要以链表作为底层结构类型进行讲解 。因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

二、队列的实现

2.1、队列的一些主要功能

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

//定义队列结构
typedef int QDataType;
typedef struct QueueNode   //其实就相当于是一个链表
{
	int data;//存储数据
	struct QueueNode* next;//指向下一个节点的指针
}QueueNode;
typedef struct Queue
{
	QueueNode* phead; //指向头节点的指针
	QueueNode* ptail;//指向尾节点的指针
	int size;//用来统计队列中的数据个数
}Queue;

//第一步:队列的初始化
void QueueInit(Queue* pd);
//入队列,队尾
void QueuePush(Queue* pd, QDataType x);
//出队列,队头
void QueuePop(Queue* pd);
//队列判空
bool QueueEmpty(Queue* pd);
//取队列头数据
QDataType QueueFront(Queue* pd);
//取队尾数据
QDataType QueueBack(Queue* pd);
//判断队列中的有效元素
int QueueSize(Queue* pd);//这里因为返回的肯定是一个整形的数据,所以直接使用int进行定义
//队列的销毁
void QueueDestroy(Queue* pd);

 从上述代码来看,我们可以看出队列这里给出的共有7个功能现在我们一个一个展开来讲。

2.2.1 入队列
void QueuePush(Queue* pd, QDataType x)
{
	assert(pd);
	//申请一个新的节点
	QueueNode* NewNode = (QueueNode*)malloc(sizeof(QueueNode));
	if (NewNode == NULL)
	{
		perror("malloc fail");
		exit(1);
	}
	//申请完新节点之后一定要记得初始化链表,将X赋给data,next指针置为空
	NewNode->data = x;
	NewNode->next = NULL;
	//先判断队列是不是为空
	if (pd->phead == NULL)
	{
		pd->phead = pd->ptail = NewNode;
	}
	//队列不为空,将NewNode赋值给ptail
	else
	{
		pd->ptail->next= NewNode;
		pd->ptail = NewNode;//将ptail移到新节点的位置
	}
	pd->size++;
}

首先我们先判断接收的指针是否为一个空指针,如果为空指针就会触发断言报错。好,现在来讲解代码,首先我们插入一个新的数据我们需要申请一个新的节点,那么申请新的节点这里我们需要用到malloc,申请多大的空间呢?申请一个QueueNode 这个链表结构体大小的空间。申请完空间之后,我们要将值赋给这个新节点下面的data,同时将新节点的下一个数据置空,那么我这里就画个图演示一下。

画得有点粗糙,不过也可以方便理解,当我们插入一个新节点之后,我们需要将ptail(尾节点)的下一个节点指向新节点,然后将ptail移到新的节点。

可以看到,我么已经成功插入了四个数据。

2.2.2 出队列
void QueuePop(Queue* pd)
{	
	assert(pd);
	//判断队列是否为空
	assert(!QueueEmpty(pd));

	//只剩一个节点的情况,避免ptail成为野指针
	if (pd->phead == pd->ptail)
	{
		free(pd->phead);
		pd->phead = pd->ptail = NULL;
	}
	else
	{
		//删除第一个节点的元素
		QueueNode* next = pd->phead->next;
		free(pd->phead);
		pd->phead = next;
	}
	pd->size--;
}

那么出队列我们是从队头进行出队列。由于是删除队头的一个数据,所以我们不需要再进行申请一个新的空间,但是我们首先要进行判断这个队列它本身是不是一个空的队列,所以我们首先要进行判空,如果是一个空队列,那么就无法进行出队列的操作。但是出队列的过程中我们需要考虑到两种情况,首先就是如果队列只有一个数据了我们该怎么办,另外一种当然就是还有很多数据的情况啦。假设现在我们只有一个数据了,那么就是ptail = phead(首节点)的情况,那么这个时候我们释放掉ptail或者phead都是可以的,因为都在同一个位置,那么释放完之后我们记得要给ptail和phead置空,避免野指针的出现。那么数据较多的情况,我们首先新建立一个指针next,将phead的下一个节点存储到next里面,防止释放掉phead之后找不到phead的下一个节点,释放完之后我们再将next的值赋给phead就可以了。这里我就不演示了,大家可以自行进行调试。

2.2.3 队列判空 
bool QueueEmpty(Queue* pd)
{
	assert(pd);
	return pd->phead == NULL && pd->ptail == NULL;
}

那么,队列判空就非常的简单啦,只要phead和ptail都为空,那么队列就会为空。这里我们使用的是一个布尔类型,当队列为空时返回true,不是则返回false。

2.2.4 取队头数据和取队尾数据
QDataType QueueFront(Queue* pd)
{
	assert(pd);//不可以传进来一个空指针
	assert(!QueueEmpty(pd));//队列里面要有数据,没有数据无法读取
	return pd->phead->data;
}

//取出队尾数据
QDataType QueueBack(Queue* pd)
{
	assert(pd);
	assert(!QueueEmpty(pd));
	return pd->ptail->data;
}

由于取出队头和队尾的数据这个过程比较的简单,我就把它们俩放到一起来讲了。同样我们要判断这是不是一个空队列,如果是一个空队列我们又怎么取得出里面的数据呢?取出队头我们就返回头节点对应的数据就可以了,队尾同样的。

2.2.5 判断队列中的数据个数
int QueueSize(Queue* pd)
{
	//int size = 0;
	//assert(pd);
	//QueueNode* pcur = pd->phead;//定义一个指针指向头节点
	//while (pcur)//当PCUR为空指针的时候跳出循环
	//{
	//	size++;
	//	pcur = pcur->next;
	//}
	return pd->size;
}

判断元素个数有两种方法,其中一种是遍历,另外一种就是在队列的结构体中在加上一个成员size。我选择后者的原因是第二种效率会更高。假设我们使用第一种遍历的方法去判断有效数据个数,那么我们每一次判断都要进行一次遍历,假设我们现在有N个数据,那么时间复杂度就会是O(N),可见效率是非常慢的,所以我们这里选择使用size。在插入数据的时候size++,在删除数据的时候size--。大家可以回过头看看插入和删除的代码是不是最后一行都有一个size;

2.2.6 队列销毁
void QueueDestroy(Queue* pd)
{
	assert(pd);
	assert(!QueueEmpty(pd));//如果队列本身就为空,就没有必要再进行销毁了
	QueueNode* pcur = pd->phead; //定义一个PCUR指针,然后循环去销毁队列
	while (pcur)
	{
		QueueNode* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pd->phead = pd->ptail = NULL;
	pd->size = 0;
}

队列的销毁稍微复杂一点点,首先当然还是要判断这是不是一个空的队列,如果本身就是一个空的队列,那我们还销毁个der啊。在这里我们定义一个新的指针pcur 同时指向第一个phead节点。开始循环进行销毁,当pcur为空时,结束循环。首先我们要将pcur->next 给next,防止pcur被释放之后找不到下一个节点,然后将next重新给pcur。最后销毁完成之后,我们要记得给phead和ptail置空,同时size置0。

总结 

好了,队列的数据结构到这里就差不多结束了,总体来说当我们有顺序表和链表的基础之后,队列这个数据结构相对来说还是非常简单的。最后大家一定要多动手操作,一定要自己手敲一遍。

       

求三连!!!!!!

  • 42
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 16
    评论
评论 16
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值