【数据结构】队列

目录

个人主页,点击这里~
数据结构专栏,点击这里~

一、队列

1、概念与结构

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

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

队列底层结构选择 :队列可以用数组链表 的结构实现,使用链表 的结构实现更优⼀些,因为如果使用数组 的结构,出队列在数组头上出数据,效率会比较低。所以我们将用链表实现队列这一数据结构。

2、队列的实现

我们要用链表实现队列 ,首先就要考虑先进先出这种模式下,链表 该如何实现?

我们可以定义一个生成队列结点的结构体 ,生成一个结点 看守队头,再生成一个结点 看守队尾,这样我们访问队头和队尾的时候都会比较简单且节省时间,然后将这两个结点包装在一个队列结构体里面。

c 复制代码
typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QN;
//队列的结构
typedef struct Queue
{
	QN* head;
	QN* tail;
}Queue;

3、队列的初始化

队列的初始化的时候,我们只需要将队列中的两个结点headtail置为空即可。

c 复制代码
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}

4、打印队列数据

c 复制代码
//队列打印
void QueuePrint(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}

5、入队

队列入队的时候是从队尾入的 。在入队之前,我们要定义一个新节点 newNode储存新数据,再执行入队操作。

c 复制代码
//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{
	//定义新节点
	QN* newNode = (QN*)malloc(sizeof(QN));
	if (newNode == NULL)
	{
		perror("malloc fail!");
		return 1;
	}
	newNode->data = x;
	newNode->next = NULL;
	//入队
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}

测试代码:

c 复制代码
#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
}

int main()
{
	test();

	return 0;
}

测试结果:

6、销毁队列

我们在单链表博客 中实现销毁链表 的时候,是一个节点一个节点进行销毁 的,所以我们在队列的销毁过程也一样,另外在销毁队列的时候,我们要保存要销毁节点的下一个节点,以免找不到下一个节点。

c 复制代码
//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;//指向队列的队头
	while (pcur)
	{
		QN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->head = pq->tail = NULL;
}

注意 :这里不用把pq也释放掉,因为pq是队列的结构体,它不是动态内存开辟出来的,只有节点才是动态内存开辟出来的,它们才需要释放。

销毁测试结果:

从右面可以看出所有的节点都被置为了空,队列已经完全被销毁了。

7、队列判空

我们接下来就要实现出队操作 ,在出队操作 之前我们还需要判断队列是不是空,如果为空就不能进行出队列操作。

c 复制代码
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}

这里要用到bool类型,所以要包含头文件#include <stdbool.h>

8、出队

出队时我们要把第一个节点销毁掉

c 复制代码
//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//队头和队尾相等,即只有一个节点
	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QN* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}

9、取队头、队尾数据

取队头数据:

c 复制代码
//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->head->data;
}

取队尾数据

c 复制代码
//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}

10、队列中有效元素个数

c 复制代码
//队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	//方法一:遍历队列(适用于不频繁使用数据个数的情况)
	QN* pcur = pq->head;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
	//方法二:在队列的结构体中添加变量size
	// 并在初始化时初始化为0,每当添加节点时就让它计数
	//(适用于频繁使用数据个数的情况)
	//return pq -> size;
}

测试代码

c 复制代码
#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
	printf("%d",QueueSize(&plist));
	QueueDestroy(&plist);
}

int main()
{
	test();

	return 0;
}

测试结果:

二、源码

Queue.h

c 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

typedef int QDataType;
//队列结点的结构
typedef struct QueueNode
{
	QDataType data;
	struct QueueNode* next;
}QN;
//队列的结构
typedef struct Queue
{
	QN* head;
	QN* tail;
}Queue;
//队列的初始化
void QueueInit(Queue* pq);
//队列打印
void QueuePrint(Queue* pq);
//入队---队尾
void QueuePush(Queue* pq, QDataType x);
//销毁队列
void QueueDestroy(Queue* pq);
//队列判空
bool QueueEmpty(Queue* pq);
//出队---队头
void QueuePop(Queue* pq);
//取队头数据
QDataType QueueFront(Queue* pq);
//取队尾数据
QDataType QueueBack(Queue* pq);
//队列中有效元素个数
int QueueSize(Queue* pq);

Queue.c

c 复制代码
#include "Queue.h"

//队列的初始化
void QueueInit(Queue* pq)
{
	assert(pq);
	pq->head = NULL;
	pq->tail = NULL;
}
//队列打印
void QueuePrint(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;
	while (pcur)
	{
		printf("%d -> ", pcur->data);
		pcur = pcur->next;
	}
	printf("NULL\n");
}
//入队---队尾
void QueuePush(Queue* pq,QDataType x)
{
	//定义新节点
	QN* newNode = (QN*)malloc(sizeof(QN));
	if (newNode == NULL)
	{
		perror("malloc fail!");
		return 1;
	}
	newNode->data = x;
	newNode->next = NULL;
	//入队
	if (pq->tail == NULL)
	{
		pq->head = pq->tail = newNode;
	}
	else
	{
		pq->tail->next = newNode;
		pq->tail = newNode;
	}
}
//销毁队列
void QueueDestroy(Queue* pq)
{
	assert(pq);
	QN* pcur = pq->head;//指向队列的队头
	while (pcur)
	{
		QN* next = pcur->next;
		free(pcur);
		pcur = next;
	}
	pq->head = pq->tail = NULL;
}
//队列判空
bool QueueEmpty(Queue* pq)
{
	assert(pq);
	return pq->head == NULL;
}
//出队---队头
void QueuePop(Queue* pq)
{
	assert(!QueueEmpty(pq));
	//队头和队尾相等,即只有一个节点
	if (pq->head == pq->tail)
	{
		free(pq->head);
		pq->head = pq->tail = NULL;
	}
	else
	{
		QN* next = pq->head->next;
		free(pq->head);
		pq->head = next;
	}
}
//取队头数据
QDataType QueueFront(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->head->data;
}
//取队尾数据
QDataType QueueBack(Queue* pq)
{
	assert(!QueueEmpty(pq));
	return pq->tail->data;
}
//队列中有效元素个数
int QueueSize(Queue* pq)
{
	assert(pq);
	//方法一:遍历队列(适用于不频繁使用数据个数的情况)
	QN* pcur = pq->head;
	int size = 0;
	while (pcur)
	{
		size++;
		pcur = pcur->next;
	}
	return size;
	//方法二:在队列的结构体中添加变量size
	// 并在初始化时初始化为0,每当添加节点时就让它计数
	//(适用于频繁使用数据个数的情况)
	//return pq -> size;
}

test.c

c 复制代码
#include "Queue.h"

void test()
{
	Queue plist;
	QueueInit(&plist);
	QueuePush(&plist, 1);
	QueuePush(&plist, 2);
	QueuePush(&plist, 3);
	QueuePrint(&plist);
	printf("%d",QueueSize(&plist));
	QueueDestroy(&plist);
}

int main()
{
	test();

	return 0;
}

总结:
以上就是本期博客分享的全部内容啦!如果觉得文章还不错的话可以三连支持一下,你的支持就是我前进最大的动力!
技术的探索永无止境! 道阻且长,行则将至!后续我会给大家带来更多优质博客内容,欢迎关注我的CSDN账号,我们一同成长!
(~ ̄▽ ̄)~

相关推荐
zhuyixiangyyds5 小时前
day21和day22学习Pandas库
笔记·学习·pandas
每次的天空5 小时前
Android学习总结之算法篇四(字符串)
android·学习·算法
背影疾风6 小时前
C++学习之路:指针基础
c++·学习
苏克贝塔7 小时前
CMake学习--Window下VSCode 中 CMake C++ 代码调试操作方法
c++·vscode·学习
odoo中国8 小时前
深度学习 Deep Learning 第15章 表示学习
人工智能·深度学习·学习·表示学习
电星托马斯8 小时前
C++中顺序容器vector、list和deque的使用方法
linux·c语言·c++·windows·笔记·学习·程序人生
清晨朝暮8 小时前
【算法学习计划】贪心算法(下)
学习
SsummerC8 小时前
【leetcode100】每日温度
数据结构·python·leetcode
jingshaoyou8 小时前
Strongswan linked_list_t链表 注释可独立运行测试
数据结构·链表·网络安全·list
yanxy51210 小时前
【TS学习】(18)分发逆变推断
前端·学习·typescript