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

【数据结构】队列“0”基础知识讲解 + 实战演练

 一、概念与结构

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

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

队列底层结构选型

队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

我们选择链表而不选择数组有如下几点优势:

1. 内存动态分配与灵活扩展性

  • 链表的动态特性:链表在内存中是通过节点的指针连接起来的,每个节点都是在需要时通过动态内存分配(如 C 语言中的malloc)获得。这意味着链表的长度可以根据实际需求随时增加或减少,无需预先指定固定的大小。例如,在处理一个元素数量不确定的任务队列时,随着任务的不断加入和完成,链表能够轻松适应元素数量的变化,不会出现像数组那样因空间不足而需要重新分配内存并复制数据的情况。
  • 数组的固定空间限制:数组在定义时需要指定大小,一旦确定,其占用的内存空间就固定下来。如果事先分配的空间过大,会造成内存浪费;若分配空间过小,当队列元素数量超过数组容量时,就需要重新分配更大的数组,并将原数组中的元素复制到新数组中,这会增加时间和空间开销。

2. 插入和删除操作效率

  • 链表的高效插入删除:在链表中进行插入和删除操作时,只需修改相关节点的指针指向即可。对于队列的入队操作(在队尾插入元素)和出队操作(在队头删除元素),时间复杂度均为 O(1) 。以单链表实现队列为例,入队时创建新节点并将其链接到队尾,出队时将头节点指向下一个节点并释放原头节点。
  • 数组的插入删除开销:在数组中进行插入和删除操作,尤其是在数组头部或中间位置时,需要移动大量元素来保持数组的连续性。对于队列的出队操作(删除队头元素),后续元素都需要向前移动,时间复杂度为 O(n),其中 n 是队列中元素的个数。这在元素数量较多时,会显著影响操作效率。

3. 内存碎片化影响

  • 链表受碎片化影响小:链表的节点在内存中是分散存储的,即使内存中存在一些碎片化的空闲空间,也能利用这些空间来分配新的节点。只要系统还有可用内存,就能继续创建节点,不会因为内存碎片化导致无法为队列分配空间。
  • 数组受碎片化影响大:数组要求连续的内存空间,当内存出现碎片化时,即使总的空闲内存足够,但如果没有一块连续的足够大的空间,就无法为数组扩容,从而可能导致队列无法正常工作。

4. 内存使用的针对性

  • 链表按需分配:链表可以根据实际存储的元素数量来分配内存,每个节点只存储数据和指针,对于元素数量较少的队列,不会占用过多的额外空间。
  • 数组的预分配浪费:数组不管实际存储多少元素,都需要预先分配固定大小的空间,当队列中元素数量远小于数组大小时,会造成内存的浪费。

不过,链表也并非完全没有缺点,比如它相较于数组,访问元素时只能顺序遍历,无法像数组那样通过下标直接随机访问,而且每个节点需要额外存储指针,会占用一定的内存空间。

二、队列的实现(重点)

2.1、Queue.h
#define _CRT_SECURE_NO_WARNINGS
#pragma once#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>typedef int QDataType;
//定义队列结点结构
typedef struct QueueNode
{int data;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);//队列判空
bool QueueEmpty(Queue* pq);// 出队列,队头
void QueuePop(Queue* pq);//取队头数据
QDataType QueueFront(Queue* pq);//取队尾数据
QDataType QueueBack(Queue* pq);//队列有效元素个数
int QueueSize(Queue* pq);
2.2、Queue.c
#include"Queue.h"//初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QNode* pcur = pq->phead;while (pcur){Queue* next = pcur->next;free(pcur);pcur = next;}//队列销毁完成pq->phead = pq->ptail = NULL;
}// ⼊队列,队尾
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//创建值为x的结点QNode* newnode = (QNode*)malloc(sizeof(QNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//队列为空if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队列中只有一个结点if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{Queue* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}//取队头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);QNode* pcur = pq->phead;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;
}
2.3、test.c
#include"Queue.h"void test01()
{Queue q;QueueInit(&q);QueuePush(&q, 1);QueuePush(&q, 2);QueuePush(&q, 3);QueuePush(&q, 4);/*QueuePop(&q);QueuePop(&q);QueuePop(&q);QueuePop(&q);*/printf("对头: %d\n", QueueFront(&q));printf("队尾: %d\n", QueueBack(&q));printf("size: %d\n", QueueSize(&q));QueueDestroy(&q);
}int main()
{test01();return 0;
}

三、队列算法题(难点)

3.1、用队列实战栈

https://leetcode.cn/problems/implement-stack-using-queues/description/

思路:俩个队列配合使用

typedef int QDataType;
//定义队列结点结构
typedef struct QueueNode {int data;struct QueueNode* next;
}QueueNode;//定义队列结构
typedef struct Queue {QueueNode* phead;//指向对头结点的指针QueueNode* ptail;//指向队尾结点的指针//int size;//队列中有效数据个数
}Queue;//初始化队列
void QueueInit(Queue* pq)
{assert(pq);pq->phead = pq->ptail = NULL;
}//销毁队列
void QueueDestroy(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;while (pcur){struct QueueNode* next = pcur->next;free(pcur);pcur = next;}//队列销毁完成pq->phead = pq->ptail = NULL;
}// ⼊队列,队尾
void QueuePush(Queue* pq, QDataType x)
{assert(pq);//创建值为x的结点QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));if (newnode == NULL){perror("malloc fail!");exit(1);}newnode->data = x;newnode->next = NULL;//队列为空if (pq->phead == NULL){pq->phead = pq->ptail = newnode;}else{pq->ptail->next = newnode;pq->ptail = pq->ptail->next;}
}//队列判空
bool QueueEmpty(Queue* pq)
{assert(pq);return pq->phead == NULL;
}// 出队列,队头
void QueuePop(Queue* pq)
{assert(!QueueEmpty(pq));//队列中只有一个结点if (pq->phead == pq->ptail){free(pq->phead);pq->phead = pq->ptail = NULL;}else{struct QueueNode* next = pq->phead->next;free(pq->phead);pq->phead = next;}
}//取队头数据
QDataType QueueFront(Queue* pq)
{assert(!QueueEmpty(pq));return pq->phead->data;
}//取队尾数据
QDataType QueueBack(Queue* pq)
{assert(!QueueEmpty(pq));return pq->ptail->data;
}//队列有效元素个数
int QueueSize(Queue* pq)
{assert(pq);QueueNode* pcur = pq->phead;int size = 0;while (pcur){size++;pcur = pcur->next;}return size;
}//-------以上是队列的结构和常用方法-------//俩个队列实现一个栈
typedef struct {Queue q1;Queue q2;
} MyStack;//创建一个栈
MyStack* myStackCreate() {MyStack* pst = (MyStack*)malloc(sizeof(MyStack));QueueInit(&pst->q1);QueueInit(&pst->q2);return pst;
}void myStackPush(MyStack* obj, int x) {//往不为空的队列中插入数据if(!QueueEmpty(&obj->q1)){//q1QueuePush(&obj->q1,x);}else{//q2QueuePush(&obj->q2,x);}
}int myStackPop(MyStack* obj) {//找不为空队列Queue* emp = &obj->q1;Queue*noneEmp = &obj->q2;if((QueueEmpty(&obj->q2))){emp = &obj->q2;noneEmp = &obj->q1;}//不为空队列前size-1个数据挪到空队列中while(QueueSize(noneEmp) > 1){//取队头int front = QueueFront(noneEmp);//入另一个队列QueuePush(emp,front);//出队头QueuePop(noneEmp);}//不为空队列数据出队int top = QueueFront(noneEmp); QueuePop(noneEmp); return top;}//取栈顶
int myStackTop(MyStack* obj) {//找不为空队列,返回不为空队列的队尾数据 if(!QueueEmpty(&obj->q1)){return QueueBack(&obj->q1);}else{return QueueBack(&obj->q2);} 
}bool myStackEmpty(MyStack* obj) {return QueueEmpty(&obj->q1) && QueueEmpty(&obj->q2); 
}void myStackFree(MyStack* obj) {QueueDestroy(&obj->q1);QueueDestroy(&obj->q2);free(obj);obj=NULL;
}/*** Your MyStack struct will be instantiated and called as such:* MyStack* obj = myStackCreate();* myStackPush(obj, x);* int param_2 = myStackPop(obj);* int param_3 = myStackTop(obj);* bool param_4 = myStackEmpty(obj);* myStackFree(obj);
*/
3.2、用栈实现队列

https://leetcode.cn/problems/implement-queue-using-stacks/description/

思路:入数据就在一个栈里入,数据导入另一个栈,出数据就在另一个栈里出

//定义栈的结构
typedef int STDataType;
typedef struct Stack {STDataType* arr;//数组int top;//指向栈顶的位置 -- 刚好就是栈中有效数据个数int capacity;//栈的空间大小
}ST;//栈的初始化
void STInit(ST* ps)
{ps->arr = NULL;ps->top = ps->capacity = 0;
}//栈的销毁
void STDesTroy(ST* ps)
{if (ps->arr)free(ps->arr);ps->arr = NULL;ps->top = ps->capacity = 0;
}//入栈 -- 栈顶
void STPush(ST* ps, STDataType x)
{assert(ps);//判断空间是否足够if (ps->top == ps->capacity){//增容int newCapacity = ps->capacity == 0 ? 4 : 2 * ps->capacity;STDataType* tmp = (STDataType*)realloc(ps->arr, newCapacity * sizeof(STDataType));if (tmp == NULL){perror("realloc fail!");exit(1);}ps->arr = tmp;ps->capacity = newCapacity;}//空间足够ps->arr[ps->top] = x;ps->top++;
}//判断栈是否为空
bool STEmpty(ST* ps)
{assert(ps);return ps->top == 0;
}//出栈 -- 栈顶
void STPop(ST* ps)
{assert(!STEmpty(ps));//栈不为空ps->top--;
}//取栈顶元素
STDataType STTop(ST* ps)
{//栈不能为空,否则取不了assert(!STEmpty(ps));return ps->arr[ps->top - 1];
}//获取栈中有效元素个数
int STSize(ST* ps)
{assert(ps);return ps->top;
}
//-------以上是栈结构的定义和常见的方法-------typedef struct {ST pushST;ST popST;
} MyQueue;MyQueue* myQueueCreate() {MyQueue* pq = (MyQueue*)malloc(sizeof(MyQueue));STInit(&pq->pushST);STInit(&pq->popST);return pq;
}//入队列
void myQueuePush(MyQueue* obj, int x) {//往pushST中插入数据STPush(&obj->pushST,x);
}//出队列
int myQueuePop(MyQueue* obj) {//popST为空 -- 将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//去栈顶,入popST,出栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);STPop(&obj->popST);return top;
}int myQueuePeek(MyQueue* obj) {//popST为空 -- 将pushST(不为空)导入到popSTif(STEmpty(&obj->popST)){while(!STEmpty(&obj->pushST)){//去栈顶,入popST,出栈STPush(&obj->popST,STTop(&obj->pushST));STPop(&obj->pushST);}}//popST不为空直接出int top = STTop(&obj->popST);return top;
}bool myQueueEmpty(MyQueue* obj) {return STEmpty(&obj->popST) && STEmpty(&obj->pushST);
}void myQueueFree(MyQueue* obj) {STDesTroy(&obj->pushST);STDesTroy(&obj->popST);free(obj);obj = NULL;
}/*** Your MyQueue struct will be instantiated and called as such:* MyQueue* obj = myQueueCreate();* myQueuePush(obj, x);* int param_2 = myQueuePop(obj);* int param_3 = myQueuePeek(obj);* bool param_4 = myQueueEmpty(obj);* myQueueFree(obj);
*/
http://www.dtcms.com/a/524270.html

相关文章:

  • 【生活】秋冬季节,鼻子很干结痂,扣掉鼻孔干痂流血,鼻塞等护理方法
  • 网站关键词公司百度关键词查询
  • 大模型通识
  • 346. 执行操作后元素的最高频率 I
  • 一些常用的linux操作指令
  • jeecg表单设计器js增强实现效果案例;点按钮出弹框,iframe嵌套,数据传输等
  • Spring IOC源码篇八 核心方法prepareBeanFactory
  • S10--循环队列
  • 基于月尺度水分平衡模型的葡萄园规划与行间管理决策
  • 网站的前期推广网页设计与制作源代码
  • PY32F040单片机介绍(3)
  • 白云网站 建设seo信科上海城市分站seo
  • Python流程控制语法结构-选择分支新特性
  • 快速学完 LeetCode top 1~50 [特殊字符]
  • 河南网站开发培训价格商丘哪里做网站比较好
  • 【常用设计模式全解析】创建型模式(聚焦对象创建机制)、结构型模式(优化类与对象的组合关系)、行为型模式(规范对象间的交互行为)
  • MFF-YOLOv8:基于多尺度特征融合的无人机遥感图像小目标检测
  • SSM框架-MyBatis1
  • 从一开始部署Android项目Sonarqube的自动化扫码+通知+增量扫描功能(Win环境、Docker,基于Jenkins)
  • 对我单位网站进行改版苏州网站建设开发公司
  • 网站架构文案软文推广去哪个平台好
  • 【Cuda C 编程指南第7章 硬件实现】
  • 低代码开发,如何让企业应用搭建化繁为简?
  • npm使用国内淘宝镜像的方法
  • pnpm + webpack + vue 项目依赖缺失错误排查与解决
  • 北斗GNSS变形监测一体机在地质灾害和桥梁安全中的应用解析
  • C++ 中的 initializer_list 详解
  • 网站建设与管理好处网站 流量 不够用
  • 2025年第六届MathorCup大数据竞赛赛题浅析-助攻快速选题
  • 网站注册转化率网站建设营销