数据结构——队列的讲解(超详细)

前言:

  我们在之前刚讲述完对于栈的讲解,下面我们在讲另一个类似栈的数据结构——队列,它们都是线性表,但结构是大有不同,下面我们直接进入讲解!

701143ba8e234b1b958c187f409cf67b.png


目录

1.队列的概念和结构

 1.1.队列的概念

 1.2.队列的结构

2.队列的实现 (基于单链表实现)

 2.1. 队列的初始化

 2.2.入队列(尾插)

 2.3.出队列

 2.4.获取队头元素

 2.5.获取队尾元素

 2.6.队列中有效元素的个数

 2.7.队列的销毁

3.代码展示 

 3.1.Queue.h

 3.2.Queue.c

总结


 

正文:

1.队列的概念和结构

 1.1.队列的概念

  队列和栈一样也是一种线性表,只不过队列和栈是不同的,队列是从一端进入,从另一端出,就有先进先出FIFO的特点,小编之前讲过的栈,是有后进先出的特点,这就是这俩之间的不同之处。下面我们开始讲解队列的结构。

 1.2.队列的结构

b6cd910db1dc40c6b2e60e7e1ba96bd4.png

  如上图所示,队列中,插入数据的那一端,叫做队尾,删除数据的那一端叫做队头,所以队列是两头类型的结构,而栈就是一头(毕竟只可以从一头插入删除数据),以上就是队列的概念和结构,除了这些,我们更要会队列的书写 ,下面小编将要带领大家去学习队列的实现!

8599fa2455d145ccb90d8aa1d6649dcc.jpeg

2.队列的实现 (基于单链表实现)

  首先,队列和栈一样,它们都有两种实现方式,栈的时候我们选择了底层是顺序表的方式,因为栈涉及到了尾插,所以顺序表的时间复杂度比较小,但是队列不一样,队列因为是尾插头删,所以小编在本篇文章中的队列是用链表(单链表)来实现的,当然,队列也可以用顺序包实现,后面小编会在习题的分享上来告诉各位如何创建的,那么下面正式开启我们的代码之旅~下面是队列所对应的结构体,其中我们要设置一个单链表的结点从而方便表示队列的数据,然后我们为了减小时间复杂度,所以特地的表示出了头结点和尾结点(后期会说为什么这么设置),然后用size表示队列中有效元素的个数:

typedef int QDataType;
typedef struct list {                //表示单链表
	QDataType data;
	struct list* next;
}list;


typedef struct Queue {   //队列对应的结构体
	list* phead;
	list* plist;
	int size;   //表示队列有效的个数
}Queue;

 2.1. 队列的初始化

  对于队列我们首先肯定还要初始化,它的初始化还是比较简单的,就把头结点和尾结点都智为空就好,然后让队列有效元素个数为0就好了。由于难度不大,下面小编直接展示代码了:

void QueueInit(Queue* pq)
{
	pq->phead = pq->plist = NULL;
	pq->size = 0;
}

 2.2.入队列(尾插)

  我们先不讲队列的销毁,它是有一点复杂的,所以小编先讲别的,我们想讲述数据如何进入队列,这时候就用上了尾插操作,因为数据是从队伍的尾进的,对于插入结点,小编在之前就说过,此时的我们需要设置一个函数,这个函数是用来创建新结点的,对于新节点的创建小编在单链表说过,但现在小编还是多说一句吧,对于新结点的创建我们可以使用动态内存函数来创建一个新的节点,然后让新节点存放的数据变成0,下一个节点变成空,然后我们返回这个结点就好了,下面小编上代码了:

list* Queuebuynode(QDataType x)    //创建新结点的函数
{
	list* p1 = (list*)malloc(sizeof(list));
	assert(p1);
	p1->next = NULL;
	p1->data = x;
	return p1;  //别忘记返回,这个的类型是链表类型
}

  之后我们需要分为两种情况判断,如果队列中的队头为空,那么我们可以直接把新节点传给队头队尾,如果不为空的话,那么我们可以直接让队尾的下一个数据为新节点,让队尾的下一个节点在变成队尾,与当初小编写的单链表尾插差不多,不过小编当初写单链表可是没有创建链表尾,当时是用了循环才找到最后一个结点,所以我们在队列中设置队尾,是为了不在循环,减小时间复杂度,下面展示代码:

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	if (pq->phead == NULL && pq->plist == NULL)   
	{
		pq -> phead = pq -> plist = Queuebuynode(x);
	}
	else
	{
		list* newnode = Queuebuynode(x);
		pq->plist->next = newnode;
		pq->plist = newnode;
	}
	pq->size++;
}

 2.3.出队列

  我们既然讲了入队列,那么就会在讲述出队列,其实就是单链表的头删,因为前面说过队列是双向的,所以是从尾部进,头部出,此时我们需要先判断一下队头是否为空,如果为空的话那么出队列就没意义了,所以我们需要设置一个布朗类型的函数来检查一下队列是不是空的,下面小编直接给上代码:

bool panduan(Queue * ps)   //判断队列是不是空的
{
	assert(ps);
	return ps->phead == NULL && ps->plist == NULL;
}

  在判断完以后,我们就可以进行头删操作,此时我们同样也需要分为两种情况,一种情况就是队列中只有一个数据的情况,此时我们可以直接把头结点释放掉,然后把队头和队尾同时变成空就好了,另一种情况我们先保存队头下一个结点的数据,然后我们让队头直接free掉,然后我们让头结点的下一个结点变为头结点就好了,此时我们就实现了队列的出队列操作,下面是实现的代码图:

void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	if (pq->phead == pq->plist)
	{
		free(pq->phead);
		pq->phead = pq->plist = NULL;
	}
	else
	{
		list* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}

 2.4.获取队头元素

  这个操作其实是蛮简单的,因为队列就是保存着队头的,但是这里我们一定要小心踩坑,如果队伍为空的话,我们是无法获取队头元素的,所以我们需要先判定一下队列是否为空,不为空的话我们直接返回队头里面存的数据即可,下面直接给上代码图:

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->phead->data;
}

 2.5.获取队尾元素

  这个其实和获取队头数据是一样的,首先都需要判定队列是不是空的,如果不为空,我们直接返回队尾中存放的元素即可,下面给代码图:

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->plist->data;
}

 2.6.队列中有效元素的个数

  还记着小编在设置队列的时候多设置出的一个size吗?那个size的作用就是为了记录队列中有效的元素个数的,细心的读者朋友可能发现了小编在上面的入队操作时,最后写了个size++,小编那时候没说过它的作用(绝对不是忘了),就是为了这里说,我们每入队一个元素,就使队列中的元素加了一个,每次出队的时候,让size减少一个,代表着有效个数减少一个,所以此时我们想要获取队伍中有效元素的个数,直接返回size即可,下面是代码图:

int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->size;
}

 2.7.队列的销毁

  队列的销毁和栈比起来算是比较繁琐的,此时我们需要先判断一下队列是否为空,如果为空的话我们就不必销毁了,直接报警告就好,之后我们先把用一个指针保存着队头数据,以这个指针为循环条件,然后可以先保存好队头的下一个结点,之后将队头释放,让队头的下一个结点变成队头,之后经过层层循环以后,我们就可以把队列的元素层层释放,之后我们循环结束以后,再把队头给销毁,然后队列中有效的元素变成空即可,此时我们就完成了销毁功能,下面是代码:

void QueueDestroy(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	list* pour = pq->phead;
	while (pour)
	{
		list* next = pour->next;
		free(pour);
		pour = next;
	}
	pq->phead = pq->plist = NULL;
	pq->size = 0;
}

   此时我们已经将队列的一些功能给实现出来,但这些功能并不是全部,队列还能有许多功能,这里小编就先写小编学过的几种功能,至于其他功能就等着读者朋友们的探索了,那么可能很多读者朋友想要源码,小编这就把源码给发出来!

ac11b7d080df428db713506bf242d985.gif

3.代码展示 

  对了,队列小编也是分成了两个文件来书写,分别是用于声明函数的头文件,以及写函数1功能的源文件

 3.1.Queue.h

#include<stdlib.h>
#include<stdio.h>
#include<stdbool.h>
#include<assert.h>
typedef int QDataType;

typedef struct list {
	QDataType data;
	struct list* next;
}list;

typedef struct Queue {
	struct list* phead;   //队头
	struct list* plist;   //队尾
	int size;
}Queue;

//队列的初始化
void QueueInit(Queue* pq);


//入队
void QueuePush(Queue* pq, QDataType x);



//出队
void QueuePop(Queue* pq);


//判断队列是不是空的
bool panduan(Queue* ps);



//取队头的元素
QDataType QueueFront(Queue* pq);


//取队尾的元素
QDataType QueueBack(Queue* pq);


//队列有效元素的个数
int QueueSize(Queue* pq);


//销毁队列
void QueueDestroy(Queue* pq);

 3.2.Queue.c

#include"Queue.h"
void QueueInit(Queue* pq)
{
	pq -> size = 0;
	pq->phead = NULL;
	pq->plist = NULL;
}



list* Queuebuynode(QDataType x)    //创建新结点的函数
{
	list* p1 = (list*)malloc(sizeof(list));
	assert(p1);
	p1->next = NULL;
	p1->data = x;
	return p1;  //别忘记返回,这个的类型是链表类型
}

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	if (pq->phead == NULL && pq->plist == NULL)   //很简单错误,c语言是没有连等这一回事的
	{
		pq -> phead = pq -> plist = Queuebuynode(x);
	}
	else
	{
		list* newnode = Queuebuynode(x);
		pq->plist->next = newnode;
		pq->plist = newnode;
	}
	pq->size++;
}



bool panduan(Queue * ps)   //判断队列是不是空的
{
	assert(ps);
	return ps->phead == NULL && ps->plist == NULL;
}


void QueuePop(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	if (pq->phead == pq->plist)
	{
		free(pq->phead);
		pq->phead = pq->plist = NULL;
	}
	else
	{
		list* next = pq->phead->next;
		free(pq->phead);
		pq->phead = next;
	}
	pq->size--;
}


QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->phead->data;
}


QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->plist->data;
}


int QueueSize(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	return pq->size;
}



void QueueDestroy(Queue* pq)
{
	assert(pq);
	assert(!panduan(pq));
	list* pour = pq->phead;
	while (pour)
	{
		list* next = pour->next;
		free(pour);
		pour = next;
	}
	pq->phead = pq->plist = NULL;
	pq->size = 0;
}

总结

  以上就是队列的概念以及功能的实现,小编是栈和队列一起写的,写的有点快,所以可能有一些小编写的没有那么好,请各位见谅,如果文章有错误,恳请在评论区指出,小编一定及时的去更正,那么,我们下一篇文章见啦!

 c3b25ffe6c9f4730ad17161e36761821.jpeg

 

 

  • 5
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值