C语言专题:7.Queue(队列)与 Linked List(链表)

发布于:2025-06-25 ⋅ 阅读:(16) ⋅ 点赞:(0)

        在数据结构中,队列(Queue)和链表(Linked List)是两个常见的线性结构,具有不同的访问和操作特性。掌握它们的实现方式和使用场景,是深入理解 C 语言和算法设计的重要基础。


一、队列(Queue)

1.1 基本概念

​         队列是一种先进先出(FIFO)的线性数据结构。元素从队尾入队(enqueue),从队头出队(dequeue)。


1.2 循环队列的结构定义

#define MAX_SIZE 100
#define ARRAY_SIZE (MAX_SIZE + 1)

typedef struct {
    int data[ARRAY_SIZE];
    int front;
    int rear;
} Queue;
  • front:指向队头元素;

  • rear:指向下一个要插入的位置。


1.3 初始化与基本操作

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

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

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

1.4 入队操作

int enqueue(Queue *q, int value) {
    if (isFull(q)) return 0;
    q->data[q->rear] = value;
    q->rear = (q->rear + 1) % MAX_SIZE;
    return 1;
}

1.5 出队操作

int dequeue(Queue *q, int *value) {
    if (isEmpty(q)) return 0;
    *value = q->data[q->front];
    q->front = (q->front + 1) % MAX_SIZE;
    return 1;
}

1.6 循环队列特点

  • 利用模运算 (index + 1) % MAX_SIZE 实现循环;

  • 实现空间最大化利用;

  • 需要特别注意“空”与“满”的判断条件差异。

  • 预留一个空位防止混淆空与满;

  • 可存储的元素最多是 MAX_SIZE (ARRAY_SIZE-1)个。


1.7 使用示例

Queue q;
initQueue(&q);
enqueue(&q, 10);
enqueue(&q, 20);
int val;
dequeue(&q, &val);  // val = 10

二、链表(Linked List)

2.1 基本概念

链表是一种动态线性结构,通过指针连接每个节点,支持高效的插入和删除。

  • 单向链表:每个节点只包含下一个节点的指针。

  • 双向链表:每个节点包含前驱与后继指针。


2.2 单向链表节点定义

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

2.3 基本操作

创建新节点
Node* createNode(int value) {
    Node *newNode = (Node *)malloc(sizeof(Node));
    newNode->data = value;
    newNode->next = NULL;
    return newNode;
}
插入节点(头插法)
void insertFront(Node **head, int value) {
    Node *newNode = createNode(value);
    newNode->next = *head;
    *head = newNode;
}
插入节点(尾插法)
void insertEnd(Node **head, int value) {
    Node *newNode = createNode(value);
    if (*head == NULL) {
        *head = newNode;
        return;
    }
    Node *cur = *head;
    while (cur->next) cur = cur->next;
    cur->next = newNode;
}
删除节点
void deleteValue(Node **head, int value) {
    Node *cur = *head, *prev = NULL;
    while (cur && cur->data != value) {
        prev = cur;
        cur = cur->next;
    }
    if (!cur) return;
    if (!prev) *head = cur->next;
    else prev->next = cur->next;
    free(cur);
}

2.4 遍历链表

void printList(Node *head) {
    while (head) {
        printf("%d -> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

2.5 使用示例

Node *head = NULL;
insertEnd(&head, 10);
insertEnd(&head, 20);
insertFront(&head, 5);
printList(head);  // 输出: 5 -> 10 -> 20 -> NULL
deleteValue(&head, 10);
printList(head);  // 输出: 5 -> 20 -> NULL

三、双向链表(Doubly Linked List)

3.1 概念

        双向链表的每个节点有两个指针,分别指向前一个节点(prev)和后一个节点(next),支持双向遍历、双端操作


3.2 节点定义

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

3.3 插入节点(尾插法)

void insertEnd(DNode **head, int val) {
    DNode *newNode = malloc(sizeof(DNode));
    newNode->data = val;
    newNode->next = NULL;
    newNode->prev = NULL;

    if (*head == NULL) {
        *head = newNode;
        return;
    }

    DNode *cur = *head;
    while (cur->next) cur = cur->next;
    cur->next = newNode;
    newNode->prev = cur;
}

3.4 删除节点(按值)

void deleteNode(DNode **head, int val) {
    DNode *cur = *head;
    while (cur && cur->data != val) {
        cur = cur->next;
    }
    if (!cur) return;

    if (cur->prev)
        cur->prev->next = cur->next;
    else
        *head = cur->next;

    if (cur->next)
        cur->next->prev = cur->prev;

    free(cur);
}

3.5 遍历链表(正向 / 反向)

void printForward(DNode *head) {
    while (head) {
        printf("%d <-> ", head->data);
        head = head->next;
    }
    printf("NULL\n");
}

void printBackward(DNode *tail) {
    while (tail) {
        printf("%d <-> ", tail->data);
        tail = tail->prev;
    }
    printf("NULL\n");
}

3.6 使用场景

应用场景 原因/优势
双端队列(Deque) 支持头尾快速插入删除
LRU缓存淘汰策略 快速移动最近访问的节点至链表头部
回退/前进功能 如浏览器记录页历史、撤销恢复操作等

四、队列 vs 链表 对比

特性 队列(Queue) 链表(Linked List)
存储结构 顺序数组(或链式) 指针链式结构
插入/删除效率 头删尾插 O(1)(数组需注意) 任意插入删除较灵活
空间使用 固定大小(顺序队列) 动态分配,更灵活
应用场景 缓冲区、任务调度 动态数据结构、栈、队列
可扩展性 需重新分配(顺序队列) 可插入任意位置

五、扩展场景与应用技巧

  • 队列常用于生产者-消费者模型宽度优先搜索BFS

  • 链表更适合频繁插入、删除、动态增长的结构;

  • 链表可以构建:栈(stack)队列(queue)、**哈希桶(hash bucket)**等;

  • 双向链表可用于实现LRU缓存淘汰算法等。


六、内存与指针注意事项

  • 使用链表必须搭配 malloc / free 管理内存;

  • 操作链表时注意空指针(NULL)判断;

  • 队列在满与空状态容易出错,尤其是循环队列中需特别处理边界条件;

  • typedef struct Node Node; 可简化递归类型定义。


七、总结

结构 特点 推荐使用场景
Queue 先进先出、线性结构、操作固定 消息队列、BFS算法、调度器
Linked List 灵活动态结构、插入删除效率高 动态集合、频繁插入删除场景

网站公告

今日签到

点亮在社区的每一天
去签到