现在位置: 首页 > C 语言数据结构与算法 > 正文

数据结构 - 优先队列

优先队列(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;
}

基于有序链表实现

实例

#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;
}

堆(Heap)是目前实现优先队列最常用、最均衡的方案——插入和取出最高优先级元素都在 O(log n) 时间内完成。这部分内容将在第 15 章堆中详细展开。


应用场景

场景说明
任务调度操作系统根据任务优先级决定 CPU 执行顺序
Dijkstra 最短路径每次选择距离最小的未访问节点,依赖优先队列
哈夫曼编码反复取出频率最低的两个节点合并,构建最优编码树