一篇文章掌握“队列”

目录

一、队列核心概念与结构

(一)基本定义

(二)核心术语

(三)逻辑与物理结构

二、队列底层结构选型

(一)非最佳方案

(二)最佳实现方案

三、环境搭建与结构体定义

(一)工程文件结构

[(二)Queue.h 完整代码](#(二)Queue.h 完整代码)

四、核心函数的实现

(一)队列初始化(QueueInit)

(二)辅助函数

(三)队列操作

(四)队列销毁(QueueDestroy)

五、测试代码(test.c)

[六、队列算法题1 ------ 用队列实现栈](#六、队列算法题1 —— 用队列实现栈)

(一)题目描述

(二)解题思路

(三)代码实现

[七、队列算法题2 ------ 用栈实现队列](#七、队列算法题2 —— 用栈实现队列)

(一)题目描述

(二)解题思路

(三)代码实现

[八、队列算法题3 ------ 设计循环队列](#八、队列算法题3 —— 设计循环队列)

(一)题目描述

(二)循环队列核心概念

(三)数组实现核心设计

(四)关键操作实现步骤

(五)核心注意事项


一、队列核心概念与结构

(一)基本定义

队列是特殊的线性表 ,仅允许在一端(队尾)执行插入操作(入队),在另一端(队头)执行删除操作(出队),严格遵循先进先出(FIFO,First In First Out) 原则。

(二)核心术语

**1、队头:**进行删除操作的一端,数据从这里取出。

**2、队尾:**进行插入操作的一端,数据从这里存入。

**3、入队:**在队尾插入新元素的操作。

**4、出队:**在队头删除元素的操作。

**5、空队列:**无有效元素的队列

(三)逻辑与物理结构

**1、逻辑结构:**线性结构,数据元素呈线性排列。

**2、物理结构:**取决于底层实现方式,可采用数组或链表存储。

二、队列底层结构选型

(一)非最佳方案

1、使用数组实现

(1)操作特性

队尾插入数据:时间复杂度 O (1),直接在数组末尾添加元素。

队头删除数据:时间复杂度 O (n),删除头部元素后需整体移动后续数据。

(2)缺陷

存在效率瓶颈,即便反过来,变成队头插入数据、队尾删除数据,队头的时间复杂度也是O(n),元素需要整体后移,队头才可以插入元素。

所以无论头尾如何定义,也至少有一个操作的时间复杂度为 O (n),且无法通过优化规避数据挪移问题。

2、使用单链表实现

(1)操作特性

**1、头部操作(删除 / 插入):**时间复杂度 O (1),直接修改指针指向。

**2、尾部操作(插入 / 删除):**时间复杂度 O (n),需遍历至尾结点。

(2)缺陷

无论谁是单链表两端哪里是队头,处理单链表尾部元素 的时候,都会因为需要遍历得到尾结点,从而达到 O(n) 的时间复杂度。

3、使用双向链表实现

**(1)优势:**头尾操作时间复杂度均为 O (1)。

**(2)缺陷:**每个结点需额外存储前驱指针,空间开销为单链表的 2 倍(32 位系统下,单链表结点占 8 字节,双向链表节点占 16 字节),仅在必要时选用。

(二)最佳实现方案

数组的 O(n) 复杂度是无法优化的,因为这是数组本身的性质决定的;双向链表的空间浪费也是无法优化的,因为这也是双向链表的性质决定的。

那我们能够优化的就只有单链表,只要把尾部指针处理好,就可以实现尾部操作也是O(1)的复杂度,从而得到好的实现。

所以我们的解决方案是,采用 **"单链表 + 头尾指针"**的实现方式,在队列的结构体中定义队头指针与队尾指针,从而平衡时间效率与空间开销。

三、环境搭建与结构体定义

(一)工程文件结构

需创建 3 个文件,分工明确,便于维护:

**1、Queue.h:**结构体定义、函数声明、头文件引入(对外提供接口)。

**2、Queue.c:**所有函数的具体实现(内部逻辑)。

**3、test.c:**测试用例编写,验证功能正确性(调用接口)。

(二)Queue.h 完整代码
cpp 复制代码
#pragma once
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <stdbool.h>

// 1. 定义队列节点结构(单链表节点)
typedef int QDataTpe; 
typedef struct QueueNode {
    QDataTpe data;          // 存储数据
    struct QueueNode* next; // 指向下一结点的指针
} QueueNode;

// 2. 定义队列管理结构(维护头尾指针与元素个数)
typedef struct Queue {
    QueueNode* phead; // 队头指针(指向第一个元素)
    QueueNode* ptail; // 队尾指针(指向最后一个元素)
    int size;         // 有效元素个数(优化计数效率)
} Queue;

// 3. 函数声明
//1、队列的初始化与销毁
void QueueInit(Queue* pq);        // 初始化队列
void QueueDestroy(Queue* pq);     // 销毁队列

//2、辅助函数
void Print(Queue* pq);            // 打印
bool QueueEmpty(Queue* pq);       // 判空

3、队操作
void QueuePush(Queue* pq, QDataTpe x); // 入队(队尾插入)
void QueuePop(Queue* pq);         // 出队(队头删除)
QDataTpe QueueFront(Queue* pq);   // 取队头数据
QDataTpe QueueBack(Queue* pq);    // 取队尾数据
int QueueSize(Queue* pq);         // 统计有效元素个数

四、核心函数的实现

**(一)**队列初始化(QueueInit)
cpp 复制代码
void QueueInit(Queue* pq) 
{
    assert(pq); // 断言:确保pq不为空指针(避免野指针操作)
    // 空队列时,头尾指针均指向NULL,元素个数为0
    pq->phead = NULL;
    pq->ptail = NULL;
    pq->size = 0;
}
(二)辅助函数

1、打印操作(Print)

cpp 复制代码
// 打印队列
void Print(Queue* pq) 
{
    assert(pq);//判空操作
    QueueNode* tmp = pq->phead;
    printf("队列为:");
    while (tmp) {
        printf("%d -> ", tmp->data);
        tmp = tmp->next;
    }
    printf("NULL\n");
}

2、判空操作(QueueEmpty)

cpp 复制代码
// 判空:队头为空则队列空
bool QueueEmpty(Queue* pq) 
{
    assert(pq);
    return pq->phead == NULL;// 队头为空则队列空
}
(三)队列操作

1、入队操作(QueuePush)

cpp 复制代码
void QueuePush(Queue* pq, QDataTpe x) 
{
    assert(pq);
    // 创建新结点
    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;  // 原队尾的next指向新结点
        pq->ptail = newnode;        // 更新队尾指针为新结点
    }
    pq->size++; // 元素个数递增
}

2、出队操作(QueuePop)

cpp 复制代码
void QueuePop(Queue* pq) 
{
    assert(pq && !QueueEmpty(pq)); // 队列非空断言

    // 单个结点的特殊处理(避免野指针)
    if (pq->phead == pq->ptail) {
        //free的是pq->phead还是pq->ptail都是一样的,因为都是那个指针
        free(pq->phead);
        pq->phead = pq->ptail = NULL;
    }else {
        // 多个结点时,删除队头并更新指针
        QueueNode* next = pq->phead->next;
        free(pq->phead);
        pq->phead = next;
    }
    pq->size--; // 元素个数递减
}

**Tip:**单结点的情况不要忘记处理。

3、取队头/队尾元素(QueueFront & QueueBack)

cpp 复制代码
// 取队头数据(仅读取,不删除)
QDataTpe QueueFront(Queue* pq) 
{
    assert(pq && !QueueEmpty(pq)); // 队列非空断言
    return pq->phead->data;        // 直接返回队头节点的数据
}

// 取队尾数据(仅读取,不删除)
QDataTpe QueueBack(Queue* pq) 
{
    assert(pq && !QueueEmpty(pq)); // 队列非空断言
    return pq->ptail->data;        // 直接返回队尾节点的数据
}

4、统计个数(QueueSize**)**

cpp 复制代码
// 统计个数:直接返回size(O(1)效率)
int QueueSize(Queue* pq) 
{
    assert(pq);
    return pq->size;
}
(四)队列销毁(QueueDestroy)
cpp 复制代码
void QueueDestroy(Queue* pq) 
{
    assert(pq);
    QueueNode* pcur = pq->phead; 
    while (pcur != NULL) 
    {       
        QueueNode* next = pcur->next; // 暂存下一结点(避免释放后找不到)
        free(pcur);                  // 释放当前结点
        pcur = next;                 // 移动到下一结点
    }
    // 重置队列状态(避免野指针)
    pq->phead = NULL;
    pq->ptail = NULL;
    pq->size = 0;
}

五、测试代码(test.c)

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

void test_queue() 
{
    Queue q;
    // 初始化队列
    QueueInit(&q);
    printf("=== 初始化后 ===\n");
    Print(&q); // 预期:队列为:NULL(空队列)
    printf("是否为空:%s\n\n", QueueEmpty(&q) ? "是" : "否");

    // 入队操作
    QueuePush(&q, 10);
    QueuePush(&q, 20);
    QueuePush(&q, 30);
    printf("=== 入队3个元素后 ===\n");
    Print(&q); // 预期:队列为:10 -> 20 -> 30 -> NULL
    printf("队头:%d,队尾:%d,元素个数:%d\n\n", 
           QueueFront(&q), QueueBack(&q), QueueSize(&q)); // 10, 30, 3

    // 出队操作
    QueuePop(&q);
    printf("=== 出队1个元素后 ===\n");
    Print(&q); // 预期:队列为:20 -> 30 -> NULL
    printf("队头:%d,队尾:%d,元素个数:%d\n\n", 
           QueueFront(&q), QueueBack(&q), QueueSize(&q)); // 20, 30, 2

    // 继续出队至空
    QueuePop(&q);
    QueuePop(&q);
    printf("=== 出队至空后 ===\n");
    Print(&q); // 预期:队列为:NULL
    printf("是否为空:%s\n", QueueEmpty(&q) ? "是" : "否");

    // 销毁队列
    QueueDestroy(&q);
}

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

六、队列算法题1 ------ 用队列实现栈

(一)题目描述

1、题目简述

使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty),即包括以下函数:

**(1)**void push(int x) 将元素 x 压入栈顶。

**(2)**int pop() 移除并返回栈顶元素。

**(3)**int top() 返回栈顶元素。

**(4)**boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。

2、题目链接:https://leetcode.cn/problems/implement-stack-using-queues/description/

(二)解题思路

1、核心矛盾:队列 FIFO(先进先出) 特性与栈 LIFO(后进先出)特性冲突,需用两个队列(q1和q2)交替存储,模拟栈的 "后入先出"。

2、实现思路

**(1)入栈:**始终向非空队列插入数据(初始时任意选择一个队列)。

(2)出栈: 将非空队列中前 n-1 个元素转移到空队列,剩余最后一个元素即为 "栈顶",弹出该元素。【从而实现后进先出】

**(3)取栈顶:**直接返回非空队列的队尾元素(栈顶对应队列尾)。

**(4)判空:**两个队列均为空时,栈为空。

简单说:往一个队列存数据后,若想像栈一样 "后进先出" 取数据,就把前 n-1 个元素移到另一个队列,原队列剩下的最后一个元素,就是最晚存入的,取出它就实现了栈的效果。

(三)代码实现

部分相关函数此前已完成实现,此处不再过多阐释,可直接调用。做题时,需注意补充这些函数的具体实现部分。

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

// 用两个队列实现栈
typedef struct 
{
    Queue q1;
    Queue q2;
} MyStack;

// 初始化栈
MyStack* myStackCreate() 
{
    MyStack* st = (MyStack*)malloc(sizeof(MyStack));
    QueueInit(&st->q1);
    QueueInit(&st->q2);
    return st;
}

// 入栈(向非空队列插入)
void myStackPush(MyStack* obj, int x) 
{
    if (!QueueEmpty(&obj->q1)) 
    { 
        QueuePush(&obj->q1, x);  // q1非空,插入q1
    } else { 
        QueuePush(&obj->q2, x);  // q1空,插入q2
    }
}

// 出栈(转移前n-1个元素,弹出最后一个)
int myStackPop(MyStack* obj) 
{
    // 确定空队列(emp)和非空队列(nonEmp)
    Queue* emp = &obj->q1;
    Queue* nonEmp = &obj->q2;
    if (!QueueEmpty(&obj->q1)) 
    {
        emp = &obj->q2;
        nonEmp = &obj->q1;
    }

    // 转移非空队列中前size-1个元素到空队列
    while (QueueSize(nonEmp) > 1) {
        QueuePush(emp, QueueFront(nonEmp)); // 非空队列的队头插入空队列
        QueuePop(nonEmp);                   // 非空队列出队
    }

    // 弹出非空队列中最后一个元素(栈顶)
    int top = QueueFront(nonEmp);
    QueuePop(nonEmp);
    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);
}

七、队列算法题2 ------ 用栈实现队列

(一)题目描述

1、题目简述

请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty),即包括以下函数:

**(1)**void push(int x) :将元素 x 推到队列的末尾

**(2)**int pop() :从队列的开头移除并返回元素

**(3)**int peek() :返回队列开头的元素

**(4)**boolean empty(): 如果队列为空,返回 true ;否则,返回 false

2、题目链接:https://leetcode.cn/problems/implement-queue-using-stacks/description/

(二)解题思路

**1、****结构定义:**创建包含两个栈的队列结构(MyQueue),分为入栈(pushST)和出栈(popST)。

**2、入队操作:**直接将数据压入pushST。

**3、出队操作:**若popST非空,直接弹出栈顶;若为空,将pushST中所有数据转移至popST后弹出栈顶。

**4、****取队头元素:**逻辑同出队,仅返回栈顶元素不删除。

**5、****判空操作:**两个栈均为空时队列为空。

**Tip:**与上一道题目的思路就是一致的。

(三)代码实现
cpp 复制代码
typedef struct {
    Stack pushST; // 入队栈
    Stack 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中插入数据
    StackPush(&obj->pushST, x);
}

//检查popST是否为空
// 1)不为空,取栈顶
// 2)为空,pushST倒入到popST中,再取popST的栈顶
int myQueuePop(MyQueue* obj) 
{
    if (StackEmpty(&obj->popST))
    {
        //导数据
        while (!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    //popST不为空
    int top = StackTop(&obj->popST);
    StackPop(&obj->popST);
    return top;
}

//返回队头元素,逻辑同出队
int myQueuePeek(MyQueue* obj) 
{
    if (StackEmpty(&obj->popST))
    {
        //导数据
        while (!StackEmpty(&obj->pushST))
        {
            StackPush(&obj->popST, StackTop(&obj->pushST));
            StackPop(&obj->pushST);
        }
    }
    //popST不为空
    return StackTop(&obj->popST);
}

//判断队列是否为空
bool myQueueEmpty(MyQueue* obj) {
    return StackEmpty(&obj->pushST) && StackEmpty(&obj->popST);
}

void myQueueFree(MyQueue* obj) {
    STDestroy(&obj->pushST);
    STDestroy(&obj->popST);
    free(obj);
    obj = NULL;
}

八、队列算法题3 ------ 设计循环队列

(一)题目描述

1、题目简述

设计你的循环队列实现。

循环队列是一种线性数据结构 ,其操作表现基于 **FIFO(先进先出)**原则并且队尾被连接在队首之后以形成一个循环。它也被称为"环形缓冲器"。

循环队列的一个好处是我们可以利用这个队列之前用过的空间。

在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

你的实现应该支持如下操作:

**(1)**MyCircularQueue(k): 构造器,设置队列长度为 k 。

**(2)**Front: 从队首获取元素。如果队列为空,返回 -1 。

**(3)**Rear: 获取队尾元素。如果队列为空,返回 -1 。

**(4)**enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。

**(5)**deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。

**(6)**isEmpty(): 检查循环队列是否为空。

**(7)**isFull(): 检查循环队列是否已满。

2、题目链接:https://leetcode.cn/problems/design-circular-queue/description/

(二)循环队列核心概念

1、定义与核心特征

循环队列又称 "环形缓冲器",是队尾与队首逻辑相连的特殊队列,核心特征如下:

**① 操作规则:**队首(front)仅用于删除数据,队尾(rear)仅用于插入数据,遵循 FIFO 原则。

**② 空间限制:**初始化时指定固定容量,使用过程中不可扩容。

**③ 满队约束:**空间占满后需先删除数据,才能插入新数据,插入失败时返回 false。

2、两种实现方式对比

循环队列可通过数组或循环链表实现,二者各有优劣,具体对比如下:

| 对比维度 | 数组实现 | 链表实现 |
| 初始化复杂度 | O (1),单次内存分配完成 | O (n),需创建并链接所有节点 |
| 操作逻辑 | 下标加减 + 取模运算实现循环 | 指针跳转实现节点操作 |
| 空间利用率 | 连续内存,无额外指针开销 | 每个节点含数据域 + 指针域,开销较大 |
| 满队判断方式 | 牺牲一个空间或增加计数器 | 通过指针关系直接判断 |

适用场景 空间需求固定、追求高效访问的场景 空间需求灵活、插入删除频繁的场景

实际开发与算法题中,数组实现因初始化简单、操作高效更常被采用,以下重点围绕数组实现展开。

(三)数组实现核心设计

1、核心问题:空队与满队的区分

数组实现的核心难点的是:空队和满队时均可能出现 front == rear,无法直接区分。解决方案为牺牲一个空间法(不额外增加计数器,更节省内存):

**(1)实际申请空间:**k+1 个(k 为队列容量,预留 1 个空间用于区分状态)。

**(2)空队条件:**front == rear(队头与队尾指向同一位置)。

(3)满队条件:(rear + 1) % (k + 1) == front(队尾的下一个位置为队头,循环意义上)。

2、数据结构定义

需定义结构体存储核心成员,包含 4 个关键部分:

**(1)数组指针:**存储队列数据的连续内存空间。

**(2)队头指针(front):**记录队首元素下标,初始值为 0。

**(3)队尾指针(rear):**记录下一个可插入位置下标,初始值为 0。

**(4)容量(capacity):**队列实际可存储的有效数据个数(即初始化时的 k)。

cpp 复制代码
typedef struct {
    int* arr;       // 存储数据的数组
    int front;      // 队头下标
    int rear;       // 队尾下标(下一个可插入位置)
    int capacity;   // 队列容量(有效数据个数)
} MyCircularQueue;
(四)关键操作实现步骤

1、初始化(创建队列)

**(1)申请结构体空间:**为 MyCircularQueue 类型分配内存。

**(2)申请数组空间:**按capacity + 1(即 k+1)个 int 类型空间分配,预留区分空满的空间。

**(3)初始化指针:**front 和 rear 均设为 0,表示初始为空队。

cpp 复制代码
MyCircularQueue* myCircularQueueCreate(int k) 
{
    MyCircularQueue* pq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    pq->arr = (int*)malloc(sizeof(int) * (k + 1));  // 申请k+1个空间
    pq->front = 0;
    pq->rear = 0;
    pq->capacity = k;
    return pq;
}

2、状态判断(isEmpty /isFull)

**(1)判空:**直接通过front == rear判断,成立则为空队。

**(2)判满:**通过(rear + 1) % (capacity + 1) == front判断,成立则为满队。

3、插入元素(enQueue)

**功能:**向队尾插入一个元素,成功返回 true,失败返回 false。

**(1)步骤 1:**先通过满队条件判断队列是否已满,满则返回 false。

**(2)步骤 2:**将元素存入 rear 指向的位置。

**(3)步骤 3:**rear 指针后移,通过取模(rear + 1) % (capacity + 1)处理循环越界。

**(4)步骤 4:**返回 true 表示插入成功。

cpp 复制代码
bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) 
{
    // 先判断是否满队
    if (myCircularQueueIsFull(obj)) {
        return false;
    }
    // 插入数据
    obj->arr[obj->rear] = value;
    // rear指针后移(循环处理)
    obj->rear = (obj->rear + 1) % (obj->capacity + 1);
    return true;
}

4、删除元素(deQueue)

**功能:**从队首删除一个元素,成功返回 true,失败返回 false。

**(1)步骤 1:**通过空队条件判断队列是否为空,空则返回 false。

**(2)步骤 2:**front 指针后移,通过取模处理循环越界(无需实际删除数据,指针移动即表示元素失效)。

**(3)步骤 3:**返回 true 表示删除成功。

cpp 复制代码
bool myCircularQueueDeQueue(MyCircularQueue* obj) 
{
    // 先判断是否空队
    if (myCircularQueueIsEmpty(obj)) {
        return false;
    }
    // front指针后移(循环处理)
    obj->front = (obj->front + 1) % (obj->capacity + 1);
    return true;
}

5、获取队首 / 队尾元素(Front / Rear)

**(1)获取队首:**空队返回 - 1,否则返回 arr[front]。

**(2)获取队尾:**空队返回 - 1,否则需计算实际队尾位置(rear 指向的是下一个插入位置,实际队尾为 rear 的前一个位置):若 rear == 0,实际队尾为 capacity(循环到数组末尾)。否则,实际队尾为rear - 1。

cpp 复制代码
// 获取队首元素
int myCircularQueueFront(MyCircularQueue* obj)
{
    if (myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    return obj->arr[obj->front];
}

// 获取队尾元素
int myCircularQueueRear(MyCircularQueue* obj) {
    if (myCircularQueueIsEmpty(obj)) {
        return -1;
    }
    int prev = obj->rear - 1;
    // 处理rear为0的循环情况
    // 此时的队尾元素在最后,刚好就是capacity指向的数据
    if (obj->rear == 0) {
        prev = obj->capacity;
    }
    return obj->arr[prev];
}

6、销毁队列(Free)

功能:释放动态分配的内存,避免内存泄漏。

**(1)步骤 1:**释放数组指针 arr 指向的空间。

**(2)步骤 2:**释放队列结构体指针 obj 指向的空间。

cpp 复制代码
void myCircularQueueFree(MyCircularQueue* obj) 
{
    if (obj->arr != NULL) {
        free(obj->arr);
        obj->arr = NULL;  // 避免野指针
    }
    free(obj);
    obj = NULL;
}
(五)核心注意事项

1、取模运算:所有指针后移操作必须加取模 (capacity + 1),确保循环特性,避免数组越界。

2、空间预留:数组实际申请 k+1 个空间是区分空满的关键,不可省略。

**3、队尾计算:**获取队尾元素时需处理 rear=0 的边界情况,避免出现负下标

4、内存释放:销毁队列时需先释放数组空间再释放结构体空间,养成置空指针的习惯。

以上即为 一篇文章掌握"队列" 的全部内容,创作不易,麻烦三连支持一下呗~

相关推荐
ArturiaZ1 小时前
【day33】
算法
Drifter_yh2 小时前
「JVM」Java 垃圾回收机制全解析:回收算法、分代流转与 G1 收集器底层拆解
java·jvm·算法
载数而行5202 小时前
算法系列3之拓扑排序
c语言·数据结构·c++·算法·排序算法
!停2 小时前
数据结构排序算法—插入排序
数据结构·算法·排序算法
s砚山s2 小时前
代码随想录刷题——二叉树篇(二十一)
算法
谁不学习揍谁!2 小时前
基于python大数据机器学习旅游数据分析可视化推荐系统(完整系统+开发文档+部署教程+文档等资料)
大数据·python·算法·机器学习·数据分析·旅游·数据可视化
yyjtx2 小时前
DHU上机打卡D28
开发语言·c++·算法
programhelp_2 小时前
Snowflake OA 2026真题解析|3道Hard Coding全拆解,附DP+双指针实战思路
算法
2501_918126912 小时前
stm32程序是用什么语言写的?
c语言·stm32·单片机·嵌入式硬件·个人开发