【数据结构】队列及其C语言模拟实现

1.队列的定义

队列是一种一端只能插入数据,一段只能删除数据的数据结构,与栈相反队列是一种先进先出的数据结构,其实也有两边都可以插入与删除的队列这种叫做双端队列,但我这里讲的是普通的队列。

其中插入数据的一端为队尾 ,出数据的一端为队头

2.队列的模拟实现

队列这个数据结构因为两端都要使用,所以明显是使用链表来实现更好,因为如果使用数组来实现的话当某一端要插入或者删除元素的话还要把数组遍历一遍,所以我这里就使用单链表来模拟这个

数据结构

下面是这个队列的定义和我们要实现的函数:

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

typedef int DataType;

typedef struct QueueNode
{
	struct QueueNode* next;
	DataType val;
}QNode;

typedef struct Queue
{
	QNode* phead;
	QNode* ptail;
	int size;
}Queue;


void QueueInit(Queue* pst);
void QueueDestroy(Queue* pst);

void QueuePush(Queue* pst, DataType x);
void QueuePop(Queue* pst);

DataType QueueFront(Queue* pst);
DataType QueueBack(Queue* pst);

bool QueueEmpty(Queue* pst);
int QueueSize(Queue* pst);

这里我们为什么要另外定义一个结构体Queue呢?因为我们这个基于链表来实现的队列,如果不定义为一个结构体我们需要修改指针时还需要传入二级指针,这样会非常麻烦。其中phead用于表示队头,ptail用于表示队尾

这里我们另外在里面创建一个变量size用于记录我们当前的元素个数,在插入或者删除数据时让这个值相应的++或者--,这样我们就不用遍历这个链表统计个数了,将时间复杂度从O(n)优化为了O(1)的时间复杂度


初始化与销毁

cpp 复制代码
void QueueInit(Queue* pst)
{
	assert(pst);
	pst->phead = pst->ptail = NULL;
	pst->size = 0;
}

void QueueDestroy(Queue* pst)
{
	assert(pst);
	QNode* cur = pst->phead;
	while (cur)
	{
		QNode* next = cur->next;
		free(cur);
		cur = next;
	}
	pst->phead = pst->ptail = NULL;
	pst->size = 0;
}

这里也没有什么好说的,在销毁时记得保留原来要销毁的那个节点指向的下一个地址即可,免得招不到发生内存泄漏的问题

插入与删除

cpp 复制代码
void QueuePush(Queue* pst, DataType x)
{
	assert(pst);
	QNode* newnode = (QNode*)malloc(sizeof(QNode));
	if (newnode == NULL)
	{
		perror("malloc fail !");
		return;
	}
	newnode->val = x;
	newnode->next = NULL;
	if (pst->ptail == NULL)
	{
		pst->ptail = pst->phead = newnode;
	}
	else
	{
		pst->ptail->next = newnode;
		pst->ptail = newnode;
	}
	pst->size++;
}

void QueuePop(Queue* pst)
{
	assert(pst);
	assert(pst->phead);
	if (pst->phead == pst->ptail)
	{
		free(pst->phead);
		pst->phead = pst->ptail = NULL;
	}
	else
	{
		QNode* next = pst->phead->next;
		free(pst->phead);
		pst->phead = next;
	}
	pst->size--;
}

在插入时考虑一下空队列的情况即可,不是空队列则尾插到链表上完成入队列的操作

在执行出队操作时则需要考虑单节点和多节点的情况,如果是单节点的话则将头指针和尾指针都置为空,只需要释放一次内存即可,因为free()是释放指针指向的空间,当只有一个节点是head和tail指向的空间为同一块空间所见仅仅需要释放一次

别忘了让size加减


返回头尾节点

cpp 复制代码
DataType QueueFront(Queue* pst)
{
	assert(pst);
	assert(pst->phead);

	return pst->phead->val;
}

DataType QueueBack(Queue* pst)
{
	assert(pst);
	assert(pst->ptail);

	return pst->ptail->val;
}

剩下的接口都很简单,判断一下头尾指针是否为空就可


是否为空和返回元素个数

cpp 复制代码
bool QueueEmpty(Queue* pst)
{
	assert(pst);

	return pst->size == 0;
}

int QueueSize(Queue* pst)
{
	assert(pst);

	return pst->size;
}

这里就体现的我们在Queue里定义size的智慧了,不用再去遍历链表一遍


相关推荐
刘马想放假7 小时前
Modbus 全栈技术解析:TCP、RTU、ASCII、RTU over TCP
数据结构·网络协议
北域码匠1 天前
冒泡排序太慢?鸡尾酒排序双向优化,原生 C# 零第三方库完整代码
数据结构·排序算法·泛型·c# 算法·鸡尾酒排序·原生 c# 开发·冒泡排序优化·嵌入式算法
卷无止境1 天前
C++ 的Eigen 库全解析
c++
卷无止境1 天前
现代 C++特性大盘点:一门脱胎换骨的老语言
c++·后端
郝学胜_神的一滴1 天前
CMake 27:缓存变量的特性、语法、类型与实操全解
c++·cmake
博客18003 天前
酷宝的使用方法,超好用的免费界面库,C++、MFC可用
c++·mfc·界面库·库来帮·酷宝
郝学胜_神的一滴3 天前
CMake 026:属性体系精讲、四大作用域全解 & 实战代码落地
c++·cmake
众少成多积小致巨4 天前
JNI (Java Native Interface) 技术手册中文参考指南
android·java·c++
Darling噜啦啦8 天前
列表转树算法深度解析:从 Map 到 Reduce 的两种实现,面试高频考点
数据结构·算法·面试
clint4568 天前
C++进阶(1)——前景提要
c++