数据结构 - 优先队列
优先队列(Priority Queue)是队列的一种扩展形式,与普通队列的"先进先出"不同,优先队列中的每个元素都被赋予一个优先级,出队操作总是取出当前队列中优先级最高的元素。
优先队列的概念与特点
可以将其类比为医院急诊室的分诊制度——病情越紧急的患者,即使后到,也会被优先处理。
优先队列的核心操作与普通队列不同:
| 操作 | 普通队列 | 优先队列 |
|---|---|---|
| 入队 (enqueue) | 在队尾添加元素 | 插入元素并附带优先级 |
| 出队 (dequeue) | 返回队首元素(最早入队) | 返回优先级最高的元素 |
三种实现方式对比
优先队列 — 三种实现方式效率对比
无序数组
插入O(1)
出队O(n)
空间仅数据
适合插入频繁
有序链表
插入O(n)
出队O(1)
空间数据+指针
适合出队频繁
堆 (Heap) ★推荐
插入O(log n)
出队O(log n)
空间数组紧凑
适合插入出队都频繁
堆实现最优:兼顾插入与出队效率,是 Dijkstra 最短路径、哈夫曼编码等算法的核心依赖
上图对比了三种实现方式的时间复杂度差异,堆实现是最均衡的选择。
基于无序数组实现
实例
#include <stdio.h>
#define MAX 100
/* 基于无序数组的优先队列(最大优先队列:值越大优先级越高) */
struct PriorityQueue {
int items[MAX];
int size; /* 当前元素个数 */
};
void initPQ(struct PriorityQueue* pq) { pq->size = 0; }
/* 入队:直接添加到末尾,O(1) */
void enqueue(struct PriorityQueue* pq, int value) {
if (pq->size >= MAX) return;
pq->items[pq->size++] = value;
}
/* 出队:遍历查找最大值,O(n)
找到后将最后一个元素移到删除位置,size 减 1 */
int dequeue(struct PriorityQueue* pq) {
if (pq->size == 0) return -1;
/* 查找最大值的索引 */
int maxIdx = 0;
for (int i = 1; i < pq->size; i++) {
if (pq->items[i] > pq->items[maxIdx]) {
maxIdx = i;
}
}
int maxVal = pq->items[maxIdx];
/* 将最后一个元素移到被删除位置(避免大量移动) */
pq->items[maxIdx] = pq->items[--pq->size];
return maxVal;
}
int main() {
struct PriorityQueue pq;
initPQ(&pq);
enqueue(&pq, 5);
enqueue(&pq, 9);
enqueue(&pq, 3);
enqueue(&pq, 7);
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 9 */
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 7 */
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 5 */
printf("出队: %d\n", dequeue(&pq)); /* 输出: 3 */
return 0;
}
#define MAX 100
/* 基于无序数组的优先队列(最大优先队列:值越大优先级越高) */
struct PriorityQueue {
int items[MAX];
int size; /* 当前元素个数 */
};
void initPQ(struct PriorityQueue* pq) { pq->size = 0; }
/* 入队:直接添加到末尾,O(1) */
void enqueue(struct PriorityQueue* pq, int value) {
if (pq->size >= MAX) return;
pq->items[pq->size++] = value;
}
/* 出队:遍历查找最大值,O(n)
找到后将最后一个元素移到删除位置,size 减 1 */
int dequeue(struct PriorityQueue* pq) {
if (pq->size == 0) return -1;
/* 查找最大值的索引 */
int maxIdx = 0;
for (int i = 1; i < pq->size; i++) {
if (pq->items[i] > pq->items[maxIdx]) {
maxIdx = i;
}
}
int maxVal = pq->items[maxIdx];
/* 将最后一个元素移到被删除位置(避免大量移动) */
pq->items[maxIdx] = pq->items[--pq->size];
return maxVal;
}
int main() {
struct PriorityQueue pq;
initPQ(&pq);
enqueue(&pq, 5);
enqueue(&pq, 9);
enqueue(&pq, 3);
enqueue(&pq, 7);
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 9 */
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 7 */
printf("出队(优先级最高): %d\n", dequeue(&pq)); /* 输出: 5 */
printf("出队: %d\n", dequeue(&pq)); /* 输出: 3 */
return 0;
}
基于有序链表实现
实例
#include <stdio.h>
#include <stdlib.h>
struct Node {
int data;
int priority; /* 优先级(值越大优先级越高) */
struct Node* next;
};
/* 入队:按优先级降序插入,O(n) */
struct Node* enqueue(struct Node* head, int data, int priority) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->priority = priority;
newNode->next = NULL;
/* 空链表或新节点优先级最高 → 头部插入 */
if (head == NULL || head->priority < priority) {
newNode->next = head;
return newNode;
}
/* 遍历找到合适位置(保持降序) */
struct Node* cur = head;
while (cur->next != NULL && cur->next->priority >= priority) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
return head;
}
/* 出队:移除头节点(优先级最高),O(1) */
struct Node* dequeue(struct Node* head) {
if (head == NULL) return NULL;
struct Node* temp = head;
printf("出队: data=%d, priority=%d\n", temp->data, temp->priority);
head = head->next;
free(temp);
return head;
}
int main() {
struct Node* pq = NULL;
pq = enqueue(pq, 10, 1); /* 数据 10,优先级 1 */
pq = enqueue(pq, 20, 5); /* 数据 20,优先级 5 */
pq = enqueue(pq, 30, 3); /* 数据 30,优先级 3 */
pq = dequeue(pq); /* 出队: data=20, priority=5 (最高优先级) */
pq = dequeue(pq); /* 出队: data=30, priority=3 */
pq = dequeue(pq); /* 出队: data=10, priority=1 */
return 0;
}
#include <stdlib.h>
struct Node {
int data;
int priority; /* 优先级(值越大优先级越高) */
struct Node* next;
};
/* 入队:按优先级降序插入,O(n) */
struct Node* enqueue(struct Node* head, int data, int priority) {
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->priority = priority;
newNode->next = NULL;
/* 空链表或新节点优先级最高 → 头部插入 */
if (head == NULL || head->priority < priority) {
newNode->next = head;
return newNode;
}
/* 遍历找到合适位置(保持降序) */
struct Node* cur = head;
while (cur->next != NULL && cur->next->priority >= priority) {
cur = cur->next;
}
newNode->next = cur->next;
cur->next = newNode;
return head;
}
/* 出队:移除头节点(优先级最高),O(1) */
struct Node* dequeue(struct Node* head) {
if (head == NULL) return NULL;
struct Node* temp = head;
printf("出队: data=%d, priority=%d\n", temp->data, temp->priority);
head = head->next;
free(temp);
return head;
}
int main() {
struct Node* pq = NULL;
pq = enqueue(pq, 10, 1); /* 数据 10,优先级 1 */
pq = enqueue(pq, 20, 5); /* 数据 20,优先级 5 */
pq = enqueue(pq, 30, 3); /* 数据 30,优先级 3 */
pq = dequeue(pq); /* 出队: data=20, priority=5 (最高优先级) */
pq = dequeue(pq); /* 出队: data=30, priority=3 */
pq = dequeue(pq); /* 出队: data=10, priority=1 */
return 0;
}
堆(Heap)是目前实现优先队列最常用、最均衡的方案——插入和取出最高优先级元素都在 O(log n) 时间内完成。这部分内容将在第 15 章堆中详细展开。
应用场景
| 场景 | 说明 |
|---|---|
| 任务调度 | 操作系统根据任务优先级决定 CPU 执行顺序 |
| Dijkstra 最短路径 | 每次选择距离最小的未访问节点,依赖优先队列 |
| 哈夫曼编码 | 反复取出频率最低的两个节点合并,构建最优编码树 |
