当前位置: 首页 > news >正文

队列概念和结构

文章目录

  • 1. 队列的概念
  • 2. 队列的分类
  • 3. 队列的实现(单向链表队列)
    • 3.1 接口设计(Queue.h)
    • 3.2 接口实现(Queue.c)
      • 1)初始化销毁
      • 2)入队
      • 3)出队
      • 4)队头尾元素
      • 5)空队列、队列长度
    • 3.3 完整代码
      • Queue.h
      • Queue.c
      • test.c
        • 注意
      • 运行效果

1. 队列的概念

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

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

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

出队顺序唯一 ,没有多种。

概念结构图

2. 队列的分类

栈的实现有3种方式:

2.1 数组队列: 队头是数组头,队尾是数组尾;但队列在数组头上出数据,效率会比较低,不合适。

2.2 链式栈:

1)双向链表实现: 队头可以是入队列也可以是出队列

2)单向链表实现: 队头是出队列,队尾是入队列(队尾可以增加个指针)

分类

双向链表毕竟比单向链表多一个指针,而且用单向链表可以实现,因此 使用单向链表实现队列 ,此处可以不带哨兵位,因为哨兵位也是个花销,产出比一般。

3. 队列的实现(单向链表队列)

下面将其分为3个模块进行实现Queue.h,Queue.c,test.c

示意图

3.1 接口设计(Queue.h)

因为要提高效率,当查看队尾元素时,单链表的时间复杂度总是 O(n) ,所以需要多一个指针指向队尾进行管理,但这样传参时就要传入二级指针(要修改指针指向,而非单纯修改结构体内部)。

因此构建一个结构体对两个指针进行管理,实现简化传参。同时,因为要记录个数,也新增 size 变量,对队列个数的管理。

接口示意图

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

typedef int QDataType;

typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* 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);

3.2 接口实现(Queue.c)

1)初始化销毁

销毁部分: 因为 pq 是外部传入的变量,不是内存开辟空间,不用对其进行 free(pq) 操作

void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
	//pq是外部传入的变量,不是内存开辟空间,不用free
}

2)入队

因为只有入队时才会插入元素,因此不用对开辟新结点操作单独做一个函数,同时,因为有两个指针进行管理,要对其进行分类讨论。

当两个指针都为空时,都要进行赋值操作;否则直接用 pq->ptail 进行链接即可。

入队

void QueuePush(Queue* pq, QDataType x)
{
	assert(pq);

	//只有入队时才会插入元素,因此不用单独做一个函数
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail\n");
		exit(-1);
	}

	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newNode;
	}
	else
	{
		pq->ptail->next = newNode;
		pq->ptail = newNode;
	}

	newNode->val = x;
	newNode->next = NULL;
	pq->size++;
}

3)出队

出队时特别要注意,如果只有一个结点,注意要将 pq->ptail 也要置空,否则会出现野指针。

出队

void QueuePop(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	//防止野指针
	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

4)队头尾元素

这里直接返回头尾指针的值即可,但要 **判断头尾指针是否为空 ** 。

QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	return pq->phead->val;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->ptail);

	return pq->ptail->val;
}

5)空队列、队列长度

空队列判断队列头指针是否为空,队列长度直接返回 pq->size 即可

bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//1
	//return pq->size == 0;
	//2
	return pq->phead == NULL;
}

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

	return pq->size;
}

3.3 完整代码

Queue.h

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

typedef int QDataType;

typedef struct QueueNode
{
	QDataType val;
	struct QueueNode* 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);

Queue.c

#include "Queue.h"

//初始化销毁
void QueueInit(Queue* pq)
{
	assert(pq);

	pq->phead = NULL;
	pq->ptail = NULL;
	pq->size = 0;
}

void QueueDestroy(Queue* pq)
{
	assert(pq);

	QNode* cur = pq->phead;
	while (cur)
	{
		QNode* del = cur;
		cur = cur->next;
		free(del);
	}

	pq->phead = pq->ptail = NULL;
	pq->size = 0;
	//pq是外部传入的变量,不是内存开辟空间,不用free
}

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

	//只有入队时才会插入元素,因此不用单独做一个函数
	QNode* newNode = (QNode*)malloc(sizeof(QNode));
	if (newNode == NULL)
	{
		perror("malloc fail\n");
		exit(-1);
	}

	if (pq->ptail == NULL)
	{
		pq->phead = pq->ptail = newNode;
	}
	else
	{
		pq->ptail->next = newNode;
		pq->ptail = newNode;
	}

	newNode->val = x;
	newNode->next = NULL;
	pq->size++;
}

void QueuePop(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	QNode* del = pq->phead;
	pq->phead = pq->phead->next;
	free(del);
	del = NULL;

	//防止野指针
	if (pq->phead == NULL)
		pq->ptail = NULL;

	pq->size--;
}

//队头尾元素
QDataType QueueFront(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->phead);

	return pq->phead->val;
}

QDataType QueueBack(Queue* pq)
{
	assert(pq);
	//空
	assert(pq->ptail);

	return pq->ptail->val;
}

//空、队长度
bool QueueEmpty(Queue* pq)
{
	assert(pq);

	//1
	//return pq->size == 0;
	//2
	return pq->phead == NULL;
}

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

	return pq->size;
}

test.c

注意

队列和栈一样,都是只能打印一个删除一个,打印完成队列清空

#include "Queue.h"

void QueueTest()
{
	Queue q;
	QueueInit(&q);
	QueuePush(&q, 1);
	QueuePush(&q, 2);
	QueuePush(&q, 3);
	QueuePush(&q, 4);
	QueuePush(&q, 5);
	QueuePush(&q, 6);

	//队列和栈一样,都是只能打印一个删除一个
	//打印完成队列清空
	printf("队头元素:%d, 队尾元素:%d, 队列个数:%d\n",
		QueueFront(&q), QueueBack(&q), QueueSize(&q));
	while (!QueueEmpty(&q))
	{
		printf("%d ", QueueFront(&q));
		QueuePop(&q);
	}
	printf("\n销毁队列done!\n");
	QueueDestroy(&q);
}

int main()
{
	QueueTest();

	return 0;
}

运行效果

运行效果
队列还有一种实际使用结构:循环队列!
可以在 循环队列概念和结构 进行阅读!

相关文章:

  • Java 大视界 -- Java 大数据在智慧农业农产品质量追溯与品牌建设中的应用(124)
  • Spring Boot 解析 LocalDateTime 失败?Uniapp 传输时间变 1970 的原因与解决方案
  • PostgreSQL 18新特性之虚拟生成列
  • Selenium/Playwright/Cypress对比与选型指南
  • 什么是vue的keep-alive?它是如何实现的?具体缓存了什么内容?
  • Qt从入门到入土(八) -打包Qt程序
  • 通义万相 2.1 × 蓝耘智算:AIGC 界的「黄金搭档」如何重塑创作未来?
  • 18天 - 常见的 HTTP 状态码有哪些?HTTP 请求包含哪些内容,请求头和请求体有哪些类型?HTTP 中 GET 和 POST 的区别是什么?
  • 刷题统计 | 第十三届蓝桥杯省赛C++B组
  • AI写论文提示词指令大全,快速写论文
  • 数据库学习笔记
  • 系统架构设计师-第5章 计算机网络
  • 我们在开发时,什么时候用到虚函数和纯虚函数?
  • 修复ubuntu下找不到音频设备的问题
  • Docker开发工具安装大合集
  • 如何撰写专业技术书籍的序言?——完整指南
  • FreeRTOS概述
  • SpringBoot 接入 豆包 火山方舟大模型
  • 共享内存的原理和创建
  • #函数探幽
  • 水利部:山西、陕西等地旱情将持续
  • 扶桑谈|素称清廉的石破茂被曝受贿,日本政坛或掀起倒阁浪潮
  • 百济首次实现季度营业利润扭亏,泽布替尼销售额近57亿元
  • 数据中心业务今年预增50%,丹佛斯:中国是全球最重要的市场
  • 以总理:在加沙地带扩大的军事行动将是“高强度”的
  • 一周观展|上海浦东美术馆透纳展还剩最后5天