【数据结构】队列---超详解

目录

一.🍻前言🍻

二.🍻队列的概念及结构🍻

1.🍺队列的概念🍺

2.🍺队列的结构🍺

三.🍻队列的实现🍻

1.🍺定义结点及队列结构🍺

2.🍺队列的初始化🍺

3.🍺销毁队列🍺

4.🍺队尾入数据🍺

5.🍺队头出数据🍺

6.🍺获取队头数据🍺

7.🍺获取队尾数据🍺

8.🍺判空🍺

9.🍺获取队列数据个数🍺

四.🍻队列的整体实现🍻

1.🍺Queue.h🍺

2.🍺Queue.c🍺

3.🍺test.c🍺

刹国(结束)!!!!


一.🍻前言🍻

相信经过前面单链表,双链表,栈等数据结构的学习后,各位对数据结构也有了一定的了解。每种数据结构都有其自身的特点,而创建这些数据结构的目的都是为了更好的管理数据。那么,我们废话不多说,马上进入下一种数据结构(队列)的学习。

二.🍻队列的概念及结构🍻

1.🍺队列的概念🍺

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

入队列:进行插入操作的一端称为队尾

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

2.🍺队列的结构🍺


三.🍻队列的实现🍻

数组和链表这两种结构都可以实现队列,但是使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。对头出数据即删除对头数据,如果用数组实现,删除对头数据则需要将对头之后的数据全部往前进行覆盖,这样会大大降低程序效率,因此我们选用单链表来实现队列。

若对链表不是很清楚的小伙伴可以到博主主页考一下古,链接:http://t.csdnimg.cn/RGQWQ

1.🍺定义结点及队列结构🍺

//定义类型
//链表队列
//队列特点:先进先出,队尾入数据,队头出数据

typedef int QDataType;
typedef struct QNode
{
	QDataType data;
	struct QNode* next;
}QNode;

typedef struct Queue
{
	QNode* Phead;
	QNode* Ptail;
	int size;
}Queue;

链表的结点由数据域和指针域爱两个部分构成,这就意味着我们需要对多类数据进行操作,因此定义一个结构体,正好可以满足这一需求。队列在队尾入数据,队头出数据,因此我们单独定义一个头和尾,外加一个记录队列大小的size来定义队列,以便后续实现队列的各种功能。用类型重定义关键字typedef将数据类型以及结构体类型分别重定义为QDataType和QNode以及Queue,以便后续修改代码和操作。


2.🍺队列的初始化🍺

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->Phead = pq->Ptail = NULL;
	pq->size = 0;
}

队列的初始化非常简单,用assert对pq断言,pq是在外部定义的队列的地址,断言是为了防止使用者误传空指针。其余将Phead和Ptail置空,size赋初值为0即可。


3.🍺销毁队列🍺

//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->Phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->Phead = pq->Ptail = NULL;
	pq->size = 0;
}

看过博主写的链表的销毁的小伙伴肯定会发现,链表的销毁和这里队列的销毁是一样的,因为队列就是用链表来实现的,所以,如果你看过链表的实现,那么队列的实现对你来说肯定就是小菜一碟。还是老套路,我们用一个cur指针来遍历一遍队列,然后再用一个指针next来保存cur的下一个位置地址,将cur当前指向空间进行释放,然后再将cur往后走,循环结束后,建立的队列也就销毁了。


4.🍺队尾入数据🍺

//队尾入数据
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->Phead == NULL)
	{
		pq->Phead = pq->Ptail = newnode;
	}
	else
	{
		pq->Ptail->next = newnode;
		pq->Ptail = newnode;
	}
	pq->size++;
}

队尾入数据时,需要先动态开辟出一个新结点,以便存放数据。入数据时需要考虑两个情况,一是队列为空,二是队列里已经存在结点。当Phead为空时,说明此时队列为空,需将新节点作为队列的头结点;当Phead,Ptail不为空后,尾插一个数据,就将Ptail往后走到下一个结点,这样Ptail就永远处在尾结点上,而Phead永远处于头结点。如此,获取对头数据以及队尾数据就将极为方便。

队尾数据插入成功后,需将size累加一下,以便后续获取队列数据个数。


5.🍺队头出数据🍺

//队头出数据
void QueuePop(Queue* pq)
{
	assert(pq);
	//1.空链表
	assert(!QueueEmpty(pq));
	//2.一个结点
	//3.多个结点
	if (pq->Phead->next == NULL)
	{
		free(pq->Phead);
		pq->Phead = pq->Ptail = NULL;
	}
	else
	{
		QNode* next = pq->Phead->next;
		free(pq->Phead);
		pq->Phead = next;
	}
	pq->size--;
}

出数据即删除数据。出队头数据即删除头结点,释放头结点空间。需要注意的是,一旦队列删空后将不能再进行删除,因此需要加一个判空处理(判空函数后续讲解)。这里同样要注意一个结点和多个结点的情况,当一个结点时,进行删除,结点空间被释放后除了要将Phead置空外,还需将Ptail置空,因为一个结点时,Phead,Ptail都指向头结点,若不将Ptail置空,将会造成野指针的问题。队头出数据后,将size--,保持队列内数据个数的正确性。


6.🍺获取队头数据🍺

//获取对头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->Phead->data;
}

队列为空时,无法获取队头数据,因此需要对队列进行判空。Phead一直指向队头,前面我们已经做过解释,所以只需要返回Phead内的数据data即可。


7.🍺获取队尾数据🍺

//获取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->Ptail->data;
}

队列为空时,同样无法获取队尾数据,因此需要对队列进行判空。Ptail一直指向队尾,前面我们已经做过解释,所以只需要返回Ptail内的数据data即可。


8.🍺判空🍺

//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->Phead == NULL
		&& pq->Ptail == NULL;
}

前面我们说过,Phead一直指向队头,Ptail一直指向队尾。那么也就是说,当队列内只有一个结点时,Phead等于Ptail且不为空;当队列内存在多个结点时,Phead不等于Ptail;当队列为空时,Phead和Ptail皆为空。所以,当Phead,Ptail都为空时,表达式( pq->Phead == NULL  &&   pq->Ptail == NULL)为真,函数返回true。


9.🍺获取队列数据个数🍺

//队列数据个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

获取队列数据个数直接返回size即可,这就是为什么一开始定义队列结构体时加上size的原因。队列在进行数据的插入或是删除时,size就一直在记录着队列内的数据个数,这就使得获取队列数据个数变得十分容易。


四.🍻队列的整体实现🍻

1.🍺Queue.h🍺

头文件的包含以及类型和函数的声明

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


//定义类型
//链表队列
//队列特点:先进先出,队尾入数据,队头出数据

typedef int QDataType;
typedef struct QNode
{
	QDataType data;
	struct QNode* next;
}QNode;

typedef struct Queue
{
	QNode* Phead;
	QNode* Ptail;
	int size;
}Queue;

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

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

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

//队头出数据
void QueuePop(Queue* pq);

//获取对头数据
QDataType QueueFront(Queue* pq);

//获取队尾数据
QDataType QueueBack(Queue* pq);

//判空
bool QueueEmpty(Queue* pq);

//队列数据个数
int QueueSize(Queue* pq);


2.🍺Queue.c🍺

函数及功能的实现

#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"

//初始化队列
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->Phead = pq->Ptail = NULL;
	pq->size = 0;
}

//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QNode* cur = pq->Phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pq->Phead = pq->Ptail = NULL;
	pq->size = 0;
}

//队尾入数据
void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail");
		return;
	}
	newnode->data = x;
	newnode->next = NULL;
	if (pq->Phead == NULL)
	{
		pq->Phead = pq->Ptail = newnode;
	}
	else
	{
		pq->Ptail->next = newnode;
		pq->Ptail = newnode;
	}
	pq->size++;
}

//队头出数据
void QueuePop(Queue* pq)
{
	assert(pq);
	//1.空链表
	assert(!QueueEmpty(pq));
	//2.一个结点
	//3.多个结点
	if (pq->Phead->next == NULL)
	{
		free(pq->Phead);
		pq->Phead = pq->Ptail = NULL;
	}
	else
	{
		QNode* next = pq->Phead->next;
		free(pq->Phead);
		pq->Phead = next;
	}
	pq->size--;
}

//获取对头数据
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->Phead->data;
}

//获取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(pq);
	assert(!QueueEmpty(pq));
	return pq->Ptail->data;
}

//判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->Phead == NULL
		&& pq->Ptail == NULL;
}

//队列数据个数
int QueueSize(Queue* pq)
{
	assert(pq);
	return pq->size;
}

3.🍺test.c🍺

功能及逻辑测试

#define _CRT_SECURE_NO_WARNINGS
#include"Queue.h"

void TestQueue()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	//printf("%d ", QueueFront(&q));
	//QueuePop(&q);

	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	printf("Size:%d\n", QueueSize(&q));
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	QueueDestroy(&q);
}


int main()
{
	TestQueue();
	return 0;
}

本篇以队列的实现为主题的博客到此就要结束了,希望本篇博客可以帮助到各位小伙伴,博主码字不易,如果本篇博客对你有所帮助,还望各位小伙伴点赞👍,收藏⭐+关注,感谢各位的支持!如果有什么不明白的地方或是另有其他的高见,也可以在评论区留言,博主也会及时回复或进行更改,欢迎指正,谢谢!

刹国(结束)!!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值