数据结构 --- 队列
2026/9/2 1:16:28 网站建设 项目流程

一、队列基础概念

1. 队列的定义与特性

队列(Queue)是一种操作受限的线性表,遵循FIFO(First‑In‑First‑Out,先进先出)原则。它只允许在表的一端(队尾)进行插入操作,在另一端(队头)进行删除操作。

核心操作:

  • 入队(Enqueue):在队尾添加新元素。
  • 出队(Dequeue):从队头移除元素。

2. 队列的基本术语

  • 队头(Front):允许删除元素的一端,即最早进入队列的元素所在位置。
  • 队尾(Rear):允许插入元素的一端,即最新进入队列的元素所在位置。
  • 队列长度(Length):队列中当前元素的个数。
  • 空队列(Empty Queue):队列长度为 0 的状态。

3. 队列的常见应用场景

  • 任务调度与缓存:操作系统进程调度、打印任务队列。
  • 数据缓冲:I/O 缓冲区、消息队列(如 RabbitMQ、Kafka 等)。
  • 算法应用:广度优先搜索(BFS)遍历树或图。
  • 并发控制:多线程环境下的任务队列、请求排队。
  • 网络通信:数据包发送与接收的缓冲区。

4. 队列的两种主要实现方式

  • 顺序队列(数组实现)
    • 使用连续内存空间存储元素。
    • 普通顺序队列的缺陷:存在“假溢出”现象(队尾指针到达数组末尾,但队头前面仍有空闲空间)。
    • 改进方案:循环队列(Circular Queue),通过取模运算实现逻辑上的环形结构,充分利用存储空间。

  • 链式队列(链表实现)
    • 使用单链表(或双向链表)存储元素,每个节点包含数据域和指向下一个节点的指针。
    • 优点:动态分配内存,不存在假溢出问题;插入和删除操作时间复杂度为 O(1)。
    • 缺点:每个节点需要额外存储指针,空间开销略大;访问任意位置元素需要遍历。

5. 队列的基本操作(API)

  • 创建队列(create_queue):初始化队列结构。
  • 入队(enqueue):将元素添加到队尾。
  • 出队(dequeue):移除并返回队头元素。
  • 获取队头元素(get_front/peek):查看队头元素但不移除。
  • 判空(is_empty):检查队列是否为空。
  • 获取队列长度(get_length):返回当前队列中元素的数量。
  • 遍历(traverse/show):按顺序输出队列中的所有元素。
  • 销毁队列(destroy_queue):释放队列占用的所有内存。

6. 循环队列的关键点

  • 判空条件:队头指针 front == 队尾指针 rear。
  • 判满条件:通常采用两种策略:
    1. 牺牲一个存储单元:(rear + 1) % MAXSIZE == front。
    2. 增加一个 size 字段:记录当前元素个数,size == MAXSIZE 即为满。
  • 入队操作:rear = (rear + 1) % MAXSIZE。
  • 出队操作:front = (front + 1) % MAXSIZE。

7. 链式队列的实现要点

  • 通常使用带头节点的单链表,维护头指针(front)和尾指针(rear)。
  • 入队时,将新节点链接到 rear 之后,并更新 rear 指向新节点。
  • 出队时,删除 front 指向的节点,并更新 front 指向下一个节点。
  • 当队列为空时,front 和 rear 均指向头节点(或均为 NULL)。
  • 当队列中只有一个元素时,front 和 rear 指向同一个节点。

8. 队列的时间复杂度分析

操作顺序队列(循环队列)链式队列
入队(enqueue)O(1)O(1)
出队(dequeue)O(1)O(1)
获取队头(peek)O(1)O(1)
判空(is_empty)O(1)O(1)
遍历(traverse)O(n)O(n)

9. 队列的变体与扩展

  • 双端队列(Deque):允许在两端进行插入和删除操作。
  • 优先队列(Priority Queue):元素按优先级出队,通常用堆(Heap)实现。
  • 阻塞队列(Blocking Queue):当队列为空时,获取操作会被阻塞;当队列满时,插入操作会被阻塞。
  • 并发队列:线程安全的队列实现,如 Java 中的 ConcurrentLinkedQueue。

二、链式队列

1. 创建、判空、销毁

Queue_t *create_queue(void) { Queue_t *pq = malloc(sizeof(Queue_t)); if(NULL == pq) { printf("malloc error\n"); return NULL; } pq->phead = NULL; pq->ptail = NULL; pq->clen = 0; return pq; } int queue_is_empty(Queue_t *pq) { return NULL == pq->phead; } void destroy_queue(Queue_t *pq) { if(NULL == pq) return; while(!queue_is_empty(pq)) { dequeue(pq); } free(pq); }

2. 入队(队尾插入)

int enqueue(Queue_t *pq, int data) { if(NULL == pq) return -1; QNode_t *pnew = malloc(sizeof(QNode_t)); if(NULL == pnew) { printf("malloc error\n"); return -1; } pnew->data = data; pnew->pnext = NULL; if(queue_is_empty(pq)) { pq->phead = pnew; pq->ptail = pnew; } else { pq->ptail->pnext = pnew; pq->ptail = pnew; } pq->clen++; return 0; }

3. 出队(队头删除)

int dequeue(Queue_t *pq) { if(NULL == pq || queue_is_empty(pq)) { printf("队列空,无法出队\n"); return -1; } QNode_t *pdel = pq->phead; pq->phead = pq->phead->pnext; free(pdel); pq->clen--; //注意:删完变空队列,ptail置NULL,避免野指针 if(queue_is_empty(pq)) { pq->ptail = NULL; } return 0; }

4. 获取队头元素

int get_front(Queue_t *pq,int *val) { if(NULL == pq || queue_is_empty(pq)) return -1; *val = pq->phead->data; return 0; }

5. 遍历打印

void show_queue(Queue_t *pq) { if(queue_is_empty(pq)) { printf("队列为空\n"); return; } QNode_t *p = pq->phead; printf("队列元素:"); while(p != NULL) { printf("%d ",p->data); p = p->pnext; } printf("\n"); }

三、循环队列

1. 创建、判满、判空、销毁

SQue_t *create_seq_queue(void) { SQue_t *psq = malloc(sizeof(SQue_t)); if (NULL == psq) { printf("malloc error\n"); return NULL; } //开辟存储数据的数组空间 psq->pbase = malloc(SEQ_MAX_LEN * sizeof(Data_t)); if (NULL == psq->pbase) { printf("malloc error\n"); free(psq); //分配数组失败,要释放已经申请的SQue_t,防止内存泄漏 return NULL; } psq->head = 0; psq->tail = 0; return psq; } //队列是否满 int is_full_seq_queue(SQue_t *psq) { return (psq->tail + 1) % SEQ_MAX_LEN == psq->head; } //队列是否空 int is_empty_seq_queue(SQue_t *psq) { return psq->head == psq->tail; } void destroy_seq_queue(SQue_t *psq) { if (psq == NULL) return; free(psq->pbase); //先释放数组 free(psq); //再释放管理结构体 }

2. 入队、出队、获取队头

//入队:队尾添加元素 int push_seq_queue(SQue_t *psq, Data_t data) { if (is_full_seq_queue(psq)) { printf("队列已满,无法入队\n"); return -1; } psq->pbase[psq->tail] = data; psq->tail = (psq->tail + 1) % SEQ_MAX_LEN; return 0; } //出队:队头删除元素,pdata接收出队的数据,可以传NULL不接收 int pop_seq_queue(SQue_t *psq, Data_t *pdata) { if (is_empty_seq_queue(psq)) { printf("队列为空,无法出队\n"); return -1; } if (pdata != NULL) { *pdata = psq->pbase[psq->head]; } psq->head = (psq->head + 1) % SEQ_MAX_LEN; return 0; } //获取队头元素,不删除 int get_seq_queue_head(SQue_t *psq, Data_t *pdata) { if (is_empty_seq_queue(psq)) { return -1; } if (pdata != NULL) { *pdata = psq->pbase[psq->head]; return 0; } return -1; }

3. 遍历打印

void show_seq_queue(SQue_t *psq) { if(is_empty_seq_queue(psq)) { printf("队列为空\n"); return; } int i; for (i = psq->head; i != psq->tail; i = (i + 1) % SEQ_MAX_LEN) { printf("%d ", psq->pbase[i].num); } printf("\n"); }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询