【数据结构:C语言版】005:C语言中的队列-普通、双端与优先级实现

        队列是一种遵循先进先出(FIFO)原则的线性数据结构。它在许多场景中都有广泛应用,如任务调度、缓冲区管理等。本文将介绍三种常见的队列类型:普通队列、双端队列和优先队列,并提供它们在C语言中的实现。

1. 普通队列

        普通队列是最基本的队列类型,只允许在一端(称为队尾)插入元素,在另一端(称为队首)删除元素。

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int items[MAX_SIZE];
    int front;
    int rear;
} Queue;

void initQueue(Queue *q) {
    q->front = -1;
    q->rear = -1;
}

int isEmpty(Queue *q) {
    return q->front == -1;
}

int isFull(Queue *q) {
    return (q->rear + 1) % MAX_SIZE == q->front;
}

void enqueue(Queue *q, int value) {
    if (isFull(q)) {
        printf("Queue is full\n");
        return;
    }
    if (isEmpty(q)) {
        q->front = 0;
    }
    q->rear = (q->rear + 1) % MAX_SIZE;
    q->items[q->rear] = value;
}

int dequeue(Queue *q) {
    if (isEmpty(q)) {
        printf("Queue is empty\n");
        return -1;
    }
    int item = q->items[q->front];
    if (q->front == q->rear) {
        // Last element
        q->front = -1;
        q->rear = -1;
    } else {
        q->front = (q->front + 1) % MAX_SIZE;
    }
    return item;
}

int main() {
    Queue q;
    initQueue(&q);

    enqueue(&q, 10);
    enqueue(&q, 20);
    enqueue(&q, 30);

    printf("Dequeued: %d\n", dequeue(&q));
    printf("Dequeued: %d\n", dequeue(&q));

    return 0;
}

/* 运行结果:
Dequeued: 10
Dequeued: 20
*/

这个实现使用了循环数组来避免假溢出问题,使得队列可以更有效地利用数组空间。

应用场景

  1. 进程调度:操作系统使用队列来管理等待执行的进程。
  2. 缓冲区管理:在数据传输中,队列用于管理数据缓冲区,如打印机任务队列。
  3. 广度优先搜索(BFS):在图的遍历算法中,BFS 使用队列来跟踪待访问的节点。
  4. 消息队列:在分布式系统中,消息队列用于在不同组件之间异步传递消息。

2. 双端队列

        双端队列(Deque)允许在队列的两端进行插入和删除操作,提供了更大的灵活性。

#include <stdio.h>
#include <stdlib.h>

typedef struct Node {
    int data;
    struct Node *prev;
    struct Node *next;
} Node;

typedef struct {
    Node *front;
    Node *rear;
} Deque;

void initDeque(Deque *d) {
    d->front = d->rear = NULL;
}

int isEmpty(Deque *d) {
    return d->front == NULL;
}

void insertFront(Deque *d, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = value;
    newNode->prev = NULL;
    newNode->next = d->front;

    if (isEmpty(d)) {
        d->rear = newNode;
    } else {
        d->front->prev = newNode;
    }
    d->front = newNode;
}

void insertRear(Deque *d, int value) {
    Node *newNode = (Node*)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = NULL;
    newNode->prev = d->rear;

    if (isEmpty(d)) {
        d->front = newNode;
    } else {
        d->rear->next = newNode;
    }
    d->rear = newNode;
}

int deleteFront(Deque *d) {
    if (isEmpty(d)) {
        printf("Deque is empty\n");
        return -1;
    }
    Node *temp = d->front;
    int value = temp->data;
    d->front = d->front->next;
    if (d->front == NULL) {
        d->rear = NULL;
    } else {
        d->front->prev = NULL;
    }
    free(temp);
    return value;
}

int deleteRear(Deque *d) {
    if (isEmpty(d)) {
        printf("Deque is empty\n");
        return -1;
    }
    Node *temp = d->rear;
    int value = temp->data;
    d->rear = d->rear->prev;
    if (d->rear == NULL) {
        d->front = NULL;
    } else {
        d->rear->next = NULL;
    }
    free(temp);
    return value;
}

int main() {
    Deque d;
    initDeque(&d);

    insertFront(&d, 10);
    insertRear(&d, 20);
    insertFront(&d, 5);

    printf("Delete front: %d\n", deleteFront(&d));
    printf("Delete rear: %d\n", deleteRear(&d));

    return 0;
}

/* 运行结果:
Delete front: 5
Delete rear: 20
*/

这个实现使用了双向链表,允许在两端高效地进行插入和删除操作。

应用场景

  1. 工作窃取调度算法:在并行计算中,双端队列用于实现工作窃取算法。
  2. 撤销操作:在文本编辑器中,双端队列可以用来实现撤销和重做功能。
  3. 滑动窗口问题:在处理固定大小的滑动窗口时,双端队列很有用。
  4. 回文检查:双端队列可以有效地用于检查一个字符串是否为回文。

3. 优先队列

        优先队列中,元素被赋予优先级。当访问元素时,具有最高优先级的元素最先删除。这里我们使用最大堆来实现最大优先队列。

#include <stdio.h>
#include <stdlib.h>

#define MAX_SIZE 100

typedef struct {
    int heap[MAX_SIZE];
    int size;
} PriorityQueue;

void initPQ(PriorityQueue *pq) {
    pq->size = 0;
}

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

void heapifyUp(PriorityQueue *pq, int index) {
    int parent = (index - 1) / 2;
    if (index > 0 && pq->heap[index] > pq->heap[parent]) {
        swap(&pq->heap[index], &pq->heap[parent]);
        heapifyUp(pq, parent);
    }
}

void heapifyDown(PriorityQueue *pq, int index) {
    int largest = index;
    int left = 2 * index + 1;
    int right = 2 * index + 2;

    if (left < pq->size && pq->heap[left] > pq->heap[largest]) {
        largest = left;
    }
    if (right < pq->size && pq->heap[right] > pq->heap[largest]) {
        largest = right;
    }

    if (largest != index) {
        swap(&pq->heap[index], &pq->heap[largest]);
        heapifyDown(pq, largest);
    }
}

void insert(PriorityQueue *pq, int value) {
    if (pq->size >= MAX_SIZE) {
        printf("Priority Queue is full\n");
        return;
    }
    pq->heap[pq->size] = value;
    heapifyUp(pq, pq->size);
    pq->size++;
}

int extractMax(PriorityQueue *pq) {
    if (pq->size <= 0) {
        printf("Priority Queue is empty\n");
        return -1;
    }
    int max = pq->heap[0];
    pq->heap[0] = pq->heap[pq->size - 1];
    pq->size--;
    heapifyDown(pq, 0);
    return max;
}

int main() {
    PriorityQueue pq;
    initPQ(&pq);

    insert(&pq, 10);
    insert(&pq, 30);
    insert(&pq, 20);
    insert(&pq, 5);

    printf("Highest priority: %d\n", extractMax(&pq));
    printf("Next highest priority: %d\n", extractMax(&pq));

    return 0;
}

/* 运行结果:
Highest priority: 30
Next highest priority: 20
*/

        这个优先队列实现使用了最大堆,确保具有最高优先级(这里用数值大小表示优先级)的元素总是在堆顶,可以在O(log n)时间内进行插入和删除操作。

应用场景

  1. 任务调度:在操作系统中,优先队列用于实现基于优先级的进程调度。
  2. Dijkstra最短路径算法:在图算法中,优先队列用于高效地选择下一个要处理的节点。
  3. 事件驱动模拟:在离散事件模拟中,优先队列用于管理按时间顺序排列的事件。
  4. 数据压缩:在Huffman编码等数据压缩算法中,优先队列用于构建Huffman树。
  5. 网络流量管理:在网络路由器中,优先队列用于实现服务质量(QoS)管理。

总结

        队列作为一种基础的数据结构,在计算机科学和软件工程中有着广泛的应用。普通队列、双端队列和优先队列各有其特点和适用场景:

  • 普通队列适用于需要按照先来先服务原则处理数据的场景。
  • 双端队列提供了更灵活的操作,适用于需要在两端进行操作的场景。
  • 优先队列在需要根据优先级处理元素的场景中非常有用。

        理解这些队列类型及其应用场景,可以帮助开发者选择合适的数据结构来解决特定的问题,从而提高程序的效率和可读性。在实际编程中,根据具体需求选择合适的队列类型,将大大提升算法的性能和代码的质量。

  • 20
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值