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

队列知识点最详细整理+总结(基于《王道数据结构考研复习指导》)

一、队列基础概念

队列是一种 先进先出(FIFO) 的线性结构,操作受限,仅允许在队尾插入(入队),队头删除(出队)。


二、顺序队列

1. 结构定义
  • 存储方式:基于数组,frontrear分别指向队头和队尾。
  • 代码实现
    #define MAX_SIZE 100
    typedef struct {
        int data[MAX_SIZE];
        int front;  // 队头指针
        int rear;   // 队尾指针
    } SeqQueue;
    
2. 优缺点
  • 优点:实现简单,访问速度快(数组随机访问)。
  • 缺点:固定大小导致空间浪费或溢出;出队需移动元素,时间复杂度高。
3. 核心操作代码
  • 入队(时间复杂度O(1)):

    int EnQueue(SeqQueue *q, int x) {
        if (q->rear == MAX_SIZE) {
            printf("队列已满");
            return 0;
        }
        q->data[q->rear++] = x;
        return 1;
    }
    
  • 出队(时间复杂度O(n),需移动元素):

    int DeQueue(SeqQueue *q, int *x) {
        if (q->front == q->rear) {
            printf("队列为空");
            return 0;
        }
        *x = q->data[0];
        for (int i = 0; i < q->rear - 1; i++) 
            q->data[i] = q->data[i + 1]; // 元素前移
        q->rear--;
        return 1;
    }
    

三、循环队列

1. 结构定义
  • 核心思想:数组首尾相连,通过取模运算实现循环,解决“假溢出”。
  • 判空/满条件
    • 队空:front == rear
    • 队满:(rear + 1) % MAX_SIZE == front(牺牲一个存储单元)。
2. 优缺点
  • 优点:空间利用率高,入队/出队O(1)。
  • 缺点:容量固定,实现复杂(需处理模运算)。
3. 核心操作代码
typedef struct {
    int data[MAX_SIZE];
    int front, rear;
} CircularQueue;

// 入队
int EnQueue(CircularQueue *q, int x) {
    if ((q->rear + 1) % MAX_SIZE == q->front) {
        printf("队列已满");
        return 0;
    }
    q->data[q->rear] = x;
    q->rear = (q->rear + 1) % MAX_SIZE;
    return 1;
}

// 出队
int DeQueue(CircularQueue *q, int *x) {
    if (q->front == q->rear) {
        printf("队列为空");
        return 0;
    }
    *x = q->data[q->front];
    q->front = (q->front + 1) % MAX_SIZE;
    return 1;
}

四、双向队列(Deque)

1. 结构定义
  • 特点:允许两端插入和删除,结合队列和栈的特性。
  • 实现方式:数组或链表(常用循环数组)。
2. 优缺点
  • 优点:操作灵活,支持随机访问(数组实现)。
  • 缺点:数组实现需处理扩容,链表实现需额外指针开销。
3. 核心操作代码(数组实现)
#define DEQUE_SIZE 100
typedef struct {
    int data[DEQUE_SIZE];
    int front, rear;
} Deque;

// 队头插入
int PushFront(Deque *dq, int x) {
    if ((dq->front - 1 + DEQUE_SIZE) % DEQUE_SIZE == dq->rear) {
        printf("队列已满");
        return 0;
    }
    dq->front = (dq->front - 1 + DEQUE_SIZE) % DEQUE_SIZE;
    dq->data[dq->front] = x;
    return 1;
}

// 队尾删除
int PopRear(Deque *dq, int *x) {
    if (dq->front == dq->rear) {
        printf("队列为空");
        return 0;
    }
    *x = dq->data[(dq->rear - 1 + DEQUE_SIZE) % DEQUE_SIZE];
    dq->rear = (dq->rear - 1 + DEQUE_SIZE) % DEQUE_SIZE;
    return 1;
}

五、时间复杂度分析

操作顺序队列循环队列双向队列(数组)
入队(尾部)O(1)O(1)O(1)
出队(头部)O(n)O(1)O(1)
随机访问O(1)O(1)O(1)

解释:顺序队列出队需移动元素,故为O(n);循环队列通过模运算避免移动,所有操作O(1)。


六、考研高频考点

  1. 循环队列的判空/满条件(如:(rear + 1) % size == front)。
  2. 双端队列的应用:滑动窗口、页面置换算法。
  3. 队列与层次遍历(BFS) :求二叉树层数、最短路径。

七、经典算法题解析(补充至3题)


题目1:用队列实现栈(LeetCode 225)

问题描述
使用两个队列实现一个后进先出(LIFO)的栈,并支持普通栈的全部操作(pushpoptopempty)。

解法思路

  • 核心矛盾:队列是FIFO,而栈是LIFO。
  • 关键操作:每次push新元素时,先将元素加入辅助队列,再将主队列中的元素依次移入辅助队列,最后交换两个队列的角色。
  • 时间复杂度push为O(n),其他操作为O(1)。

C语言代码(基于循环队列实现):

#include <stdlib.h>
#define MAX_SIZE 100

typedef struct {
    CircularQueue q1;  // 主队列
    CircularQueue q2;  // 辅助队列
} MyStack;

MyStack* myStackCreate() {
    MyStack *stack = (MyStack*)malloc(sizeof(MyStack));
    stack->q1.front = stack->q1.rear = 0;
    stack->q2.front = stack->q2.rear = 0;
    return stack;
}

void myStackPush(MyStack* obj, int x) {
    // 先将元素加入辅助队列q2
    EnQueue(&obj->q2, x);
    // 将q1中的元素全部转移到q2
    while (!QueueEmpty(&obj->q1)) {
        int val;
        DeQueue(&obj->q1, &val);
        EnQueue(&obj->q2, val);
    }
    // 交换q1和q2的角色
    CircularQueue temp = obj->q1;
    obj->q1 = obj->q2;
    obj->q2 = temp;
}

int myStackPop(MyStack* obj) {
    int val;
    DeQueue(&obj->q1, &val);
    return val;
}

int myStackTop(MyStack* obj) {
    return obj->q1.data[obj->q1.front];
}

int myStackEmpty(MyStack* obj) {
    return QueueEmpty(&obj->q1);
}

易错点

  • 队列判空条件错误:未正确实现QueueEmpty函数,导致逻辑混乱。
  • 未及时交换队列:在push操作后必须交换队列,否则后续操作会出错。

题目2:二叉树的层序遍历(LeetCode 102)

问题描述
给定一个二叉树,返回其按层序遍历得到的节点值(即逐层从左到右访问所有节点)。

解法思路

  • BFS框架:用队列存储每层的节点,每次处理一层节点时,记录当前队列长度(即该层节点数),依次出队并将子节点入队。
  • 时间复杂度:O(n),每个节点进出队列一次。

C语言代码(需结合队列结构):

typedef struct TreeNode {
    int val;
    struct TreeNode *left, *right;
} TreeNode;

int** levelOrder(TreeNode* root, int* returnSize, int** returnColumnSizes) {
    if (!root) return NULL;
    
    // 初始化队列
    CircularQueue q;
    q.front = q.rear = 0;
    EnQueue(&q, root);
    
    int**res = (int**)malloc(sizeof(int*) * MAX_SIZE);
    *returnColumnSizes = (int*)malloc(sizeof(int) * MAX_SIZE);
    *returnSize = 0;
    
    while (!QueueEmpty(&q)) {
        int level_size = (q.rear - q.front + MAX_SIZE) % MAX_SIZE;
        res[*returnSize] = (int*)malloc(sizeof(int) * level_size);
        (*returnColumnSizes)[*returnSize] = level_size;
        
        for (int i = 0; i < level_size; i++) {
            TreeNode* node;
            DeQueue(&q, &node);  // 出队当前层节点
            res[*returnSize][i] = node->val;
            if (node->left) EnQueue(&q, node->left);  // 左子节点入队
            if (node->right) EnQueue(&q, node->right); // 右子节点入队
        }
        (*returnSize)++;
    }
    return res;
}

关键点解析

  • 记录每层大小level_size用于确定当前层的节点数量,确保结果数组正确分配内存。
  • 子节点入队条件:需判断子节点非空再入队,避免队列中混入空指针。

题目3:设计循环双端队列(LeetCode 641)

问题描述
设计实现一个循环双端队列,支持以下操作:

  • insertFront():在队头添加元素。
  • insertLast():在队尾添加元素。
  • deleteFront():删除队头元素。
  • deleteLast():删除队尾元素。
  • getFront():获取队头元素。
  • getRear():获取队尾元素。
  • isEmpty():检查队列是否为空。
  • isFull():检查队列是否已满。

解法思路

  • 循环数组实现:利用取模运算处理索引循环,牺牲一个存储单元区分队空和队满。
  • 边界处理:插入和删除时需分别处理frontrear的移动方向。

C语言代码

typedef struct {
    int *data;
    int front, rear;
    int capacity;
} MyCircularDeque;

MyCircularDeque* myCircularDequeCreate(int k) {
    MyCircularDeque* dq = (MyCircularDeque*)malloc(sizeof(MyCircularDeque));
    dq->data = (int*)malloc(sizeof(int) * (k + 1)); // 多分配一个空间用于判满
    dq->front = dq->rear = 0;
    dq->capacity = k + 1;
    return dq;
}

int myCircularDequeInsertFront(MyCircularDeque* obj, int value) {
    if (myCircularDequeIsFull(obj)) return 0;
    obj->front = (obj->front - 1 + obj->capacity) % obj->capacity; // 队头前移
    obj->data[obj->front] = value;
    return 1;
}

int myCircularDequeInsertLast(MyCircularDeque* obj, int value) {
    if (myCircularDequeIsFull(obj)) return 0;
    obj->data[obj->rear] = value;
    obj->rear = (obj->rear + 1) % obj->capacity; // 队尾后移
    return 1;
}

int myCircularDequeDeleteFront(MyCircularDeque* obj) {
    if (myCircularDequeIsEmpty(obj)) return 0;
    obj->front = (obj->front + 1) % obj->capacity;
    return 1;
}

int myCircularDequeDeleteLast(MyCircularDeque* obj) {
    if (myCircularDequeIsEmpty(obj)) return 0;
    obj->rear = (obj->rear - 1 + obj->capacity) % obj->capacity;
    return 1;
}

int myCircularDequeGetFront(MyCircularDeque* obj) {
    return myCircularDequeIsEmpty(obj) ? -1 : obj->data[obj->front];
}

int myCircularDequeGetRear(MyCircularDeque* obj) {
    return myCircularDequeIsEmpty(obj) ? -1 : obj->data[(obj->rear - 1 + obj->capacity) % obj->capacity];
}

int myCircularDequeIsEmpty(MyCircularDeque* obj) {
    return obj->front == obj->rear;
}

int myCircularDequeIsFull(MyCircularDeque* obj) {
    return (obj->rear + 1) % obj->capacity == obj->front;
}

易错点

  • 索引计算错误:在队头插入时,front应前移(front - 1),而非后移。
  • 判满条件错误:必须牺牲一个存储单元,否则队空和队满条件会冲突。

八、易错点总结

  1. 循环队列的判满条件:必须牺牲一个存储单元,否则与判空条件冲突。
  2. 指针初始值frontrear通常初始化为0,但需根据实现方式调整。
  3. 边界检查:操作前必须检查队列是否为空/满,避免溢出或空指针。

九、解题方法论

  1. 选择合适结构:空间固定用循环队列,动态扩展用链队列。
  2. BFS模板:队列维护当前层节点,逐层扩展。
  3. 滑动窗口:双端队列维护极值,减少重复计算。

附:考研复习建议

  • 重点掌握:循环队列的判空判满、双端队列的应用。
  • 典型习题:设计循环队列(LeetCode 622)、二叉树的右视图(BFS应用)。
  • 易错题:循环队列长度计算 (rear - front + size) % size

通过系统梳理和代码实践,可深入理解队列的核心思想与应用场景。

相关文章:

  • 数据库基础以及基本建库建表的简单操作
  • 2025.2.4 更新 AI绘画秋葉aaaki整合包 Stable Diffusion整合包v4.10 +ComfyUI 整合包下载地址
  • Python 编写第一个网络爬虫教程
  • 短记:Flutter 项目常见问题 Gradle version is incompatible with the Java version
  • 深入解析:Linux中KVM虚拟化技术
  • Docker 学习(四)——Dockerfile 创建镜像 (详细版)
  • java每日精进 3.08 OAUTH 2.0
  • uniapp+<script setup lang=“ts“>使用 uni.$emit和uni.$on全局传递数据
  • 蓝桥杯备考:倍增算法详解
  • 深度学习模型组件之优化器-自适应学习率优化方法(Adagrad、RMSprop)
  • Windows CMD 命令大全(综合开发整理版)
  • 【Python 数据结构 8.串】
  • Mysql的行级锁到底锁住了哪些行
  • Windows 图形显示驱动开发-WDDM 3.2-本机 GPU 围栏对象(七)
  • 初阶数据结构(C语言实现)——4.1栈
  • blender学习25.3.8
  • 【Java学习笔记】三、运算符,表达式、分支语句和循环语句
  • 为什么js小数相加,会产生精度缺失的问题,怎么解决?
  • 模拟调制技术详解
  • 前后端数据加密传输【最佳方案】
  • 企业网站建设策划书范文/有哪些可以免费推广的平台
  • asp网站如何安装/欧洲站fba
  • 做兼职最靠谱的网站/百度人工客服24小时
  • 做网站链接容易吗/长沙seo优化排名
  • 丽水网站建设费用/全网
  • 2017招远网站建设/太原关键词优化报价