数据结构:队列

目录

一、【队列】

1.1 队列的定义

1.2 队列的基本操作

二、【队列的类型】

2.1 顺序队列

2.1.1 概述

2.1.2 接口实现

1. 初始化队列

2. 入队

3. 出队

4.  查看队头

5.  查看队尾

6.  检查队列是否为空

7.  获取队列大小

8.  销毁队列

2.2 循环队列

2.2.1 概述

1. 入队操作

2. 出队操作

3. 判断队列是否满

2.3 双端队列

2.3.1 概述

2.3.2 特殊的双端队列 

三、【完整代码】


一、【队列】

1.1 队列的定义

    队列是一种先进先出的数据结构。第一个入队列的元素将是第一个被移除的元素。可以将队列想象成一个排队的人,先到的人先得到服务。

    在队列中,允许插入的一端称为队尾,而允许删除的一端称为队头。当一个元素被添加到队列时,它会被放置在队尾;而当一个元素从队列中被移除时,它会从队头被取出。

1.2 队列的基本操作

队列的基本操作包括:

  • 入队(Enqueue): 将一个元素添加到队列的末尾。

  • 出队(Dequeue): 移除并返回队列的头部元素。

  • 查看队头(Peek/Front): 返回队列的头部元素,但不移除它。

  • 查看队尾(Back): 返回队列的尾部元素,但不移除它。

  • 检查队列是否为空(IsEmpty): 判断队列中是否有元素。

  • 获取队列大小(Size): 返回队列中元素的数量。

  • 初始化队列(Init): 初始化队列,准备使用。

  • 销毁队列(Destroy): 清理队列,释放相关资源。

二、【队列的类型】

注意:本文,仅实现顺序队列的完整代码

2.1 顺序队列

2.1.1 概述

顺序队列使用数组或链表来实现队列的基本操作。通过两个指针(或索引)来跟踪队头和队尾的位置,从而进行元素的入队和出队操作。

1. 使用数组实现:

#define MAX_SIZE 100 // 队列最大容量

// 顺序队列结构
typedef struct {
    int data[MAX_SIZE]; // 存储队列元素的数组
    int front;          // 队头索引
    int rear;           // 队尾索引
    int size;           // 当前队列大小
} ArrayQueue;

2. 使用链表实现:

// 节点结构
typedef struct Node {
    int data;              // 存储数据
    struct Node *next;     // 指向下一个节点的指针
} Node;

// 队列结构
typedef struct {
    Node *front; // 队头指针
    Node *rear;  // 队尾指针
    int size;    // 当前队列大小
} Queue;
2.1.2 接口实现

顺序队列的基本操作,包括初始化、入队、出队、查看队头、查看队尾、检查队列状态、获取队列大小和销毁队列的实现。

1. 初始化队列

初始化队列,设置 front 和 rear 为 NULL,队列大小为 0。

void initQueue(SeqQueue* queue) {
    queue->front = NULL;
    queue->rear = NULL;
    queue->size = 0;
}
2. 入队

在队列中添加元素,首先创建一个新节点。如果队列为空,则将 front 和 rear 都指向新节点;否则,将新节点添加到 rear 的后面,并更新 rear

bool enqueue(SeqQueue* queue, int value) {
    Node* newNode = (Node*)malloc(sizeof(Node));
    if (!newNode) {
        return false; // 内存分配失败
    }
    newNode->data = value;
    newNode->next = NULL; // 新节点的下一个节点为 NULL

    if (isEmpty(queue)) {
        queue->front = newNode; // 队列为空时,front 和 rear 都指向新节点
        queue->rear = newNode;
    } else {
        queue->rear->next = newNode; // 将新节点添加到队尾
        queue->rear = newNode; // 更新 rear 指针
    }
    queue->size++;
    return true;
}
3. 出队

从队列中移除元素,首先检查队列是否为空。如果不为空,将 front 指向的节点移除,并更新 front 指针。如果队列变为空,还需要将 rear 置为 NULL。

bool dequeue(SeqQueue* queue, int* value) {
    if (isEmpty(queue)) {
        return false; // 队列为空
    }
    Node* temp = queue->front; // 保存队头节点
    *value = temp->data; // 获取队头元素
    queue->front = queue->front->next; // 更新 front 指针

    if (queue->front == NULL) {
        queue->rear = NULL; // 如果队列变为空,更新 rear 为 NULL
    }
    free(temp); // 释放队头节点
    queue->size--;
    return true;
}
4.  查看队头

查看队头元素而不移除它。

bool peekFront(SeqQueue* queue, int* value) {
    if (isEmpty(queue)) {
        return false; // 队列为空
    }
    *value = queue->front->data; // 返回队头元素
    return true;
}
5.  查看队尾

查看队尾元素而不移除它。

bool peekBack(SeqQueue* queue, int* value) {
    if (isEmpty(queue)) {
        return false; // 队列为空
    }
    *value = queue->rear->data; // 返回队尾元素
    return true;
}
6.  检查队列是否为空

判断队列中是否有元素。

bool isEmpty(SeqQueue* queue) {
    return queue->size == 0;
}
7.  获取队列大小

返回队列中元素的数量。

int size(SeqQueue* queue) {
    return queue->size;
}
8.  销毁队列

清理队列,释放相关资源。

void destroyQueue(SeqQueue* queue) {
    while (!isEmpty(queue)) {
        int value;
        dequeue(queue, &value); // 逐个出队并释放节点
    }
}

2.2 循环队列

2.2.1 概述

循环队列是一种对顺序队列的改进,通过将队列的尾部与头部连接起来,利用数组的循环特性来避免空间浪费。

1. 入队操作

在循环队列中,入队操作的步骤如下:

queue->data[queue->rear] = value; // 插入元素
queue->rear = (queue->rear + 1) % MAX_SIZE; // 更新队尾指针
  • 插入元素queue->data[queue->rear] = value; 将新元素 value 放入 rear 指向的位置。
  • 更新队尾指针queue->rear = (queue->rear + 1) % MAX_SIZE; 使用取余运算确保 rear 在达到 MAX_SIZE 后能够回绕到数组的开头,实现循环效果。
2. 出队操作

出队操作的步骤如下:

*value = queue->data[queue->front]; // 获取队头元素
queue->front = (queue->front + 1) % MAX_SIZE; // 更新队头指针
  • 获取队头元素*value = queue->data[queue->front]; 取出 front 指向的元素。
  • 更新队头指针queue->front = (queue->front + 1) % MAX_SIZE; 更新 front 指针,以便指向下一个元素。
3. 判断队列是否满
return (queue->rear + 1) % MAX_SIZE == queue->front; // 队列满
  • 在循环队列中,队列满的条件是 rear 的下一位置与 front 相等,即:
    (rear+1)% MAX_SIZE==front(rear+1)%MAX_SIZE==front
  • 这里的 MAX_SIZE 是队列的最大容量。使用取余运算可以确保 rear 在数组的边界处回绕。

示例
假设 MAX_SIZE = 5,队列的状态如下:

队列索引01234
元素123
front0
rear12
  • 此时 rear 是 3,front 是 0。如果 rear 再增加 1,变为 4,队列仍然不满。
  • 当 rear 变为 0(即 (4 + 1) % 5),此时 front 仍然是 0,队列就满了。

2.3 双端队列

2.3.1 概述

双端队列允许在两端进行插入和删除操作。它可以通过数组或链表实现,提供灵活的操作方式。

1. 使用数组实现:使用一个固定大小的数组,并维护两个指针:front(队头指针)和 rear(队尾指针)。在实现时,需要注意数组的循环利用。

#define MAX_SIZE 100 // 定义双端队列的最大容量

typedef struct {
    int data[MAX_SIZE]; // 存储双端队列元素的数组
    int front;          // 队头指针
    int rear;           // 队尾指针
} Deque;

2. 使用链表实现:使用双向链表实现双端队列,每个节点有两个指针,分别指向前一个节点和后一个节点。

typedef struct Node {
    int data;               // 存储数据
    struct Node *next;      // 指向下一个节点的指针
    struct Node *prev;      // 指向前一个节点的指针
} Node;

typedef struct {
    Node *front; // 队头指针
    Node *rear;  // 队尾指针
} Deque;
2.3.2 特殊的双端队列 

1.输入受限:能在一端能进行插入删除操作,另一端只能进行删除操作

2.输出受限:能在一端能进行插入删除操作,另一端只能进行插入操作

三、【完整代码】

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

// 节点结构
typedef struct Node {
    int data;              // 存储数据
    struct Node *next;     // 指向下一个节点的指针
} Node;

// 队列结构
typedef struct {
    Node *front; // 队头指针
    Node *rear;  // 队尾指针
    int size;    // 当前队列大小
} Queue;

// 初始化队列
void init(Queue *queue) {
    queue->front = NULL; // 队头指针初始化为NULL
    queue->rear = NULL;  // 队尾指针初始化为NULL
    queue->size = 0;     // 当前队列大小初始化为0
}

// 销毁队列
void destroy(Queue *queue) {
    Node *current = queue->front;
    while (current != NULL) {
        Node *next = current->next; // 保存下一个节点
        free(current);               // 释放当前节点
        current = next;             // 移动到下一个节点
    }
    queue->front = NULL; // 清空队头指针
    queue->rear = NULL;  // 清空队尾指针
    queue->size = 0;     // 重置队列大小
}

// 入队(Enqueue)
bool enqueue(Queue *queue, int value) {
    Node *newNode = (Node *)malloc(sizeof(Node)); // 创建新节点
    if (newNode == NULL) {
        return false; // 内存分配失败
    }
    newNode->data = value; // 设置节点数据
    newNode->next = NULL;  // 新节点的下一个指针设为NULL

    if (queue->rear != NULL) {
        queue->rear->next = newNode; // 如果队列不为空,将新节点添加到队尾
    } else {
        queue->front = newNode; // 如果队列为空,新节点是队头
    }
    queue->rear = newNode; // 更新队尾指针
    queue->size++;         // 更新队列大小
    return true;
}

// 出队(Dequeue)
bool dequeue(Queue *queue, int *value) {
    if (queue->front == NULL) {
        return false; // 队列为空,无法出队
    }
    Node *nodeToDelete = queue->front; // 获取队头节点
    *value = nodeToDelete->data;        // 返回队头数据
    queue->front = queue->front->next; // 更新队头指针

    if (queue->front == NULL) {
        queue->rear = NULL; // 如果队列为空,队尾也设为NULL
    }

    free(nodeToDelete); // 释放队头节点
    queue->size--;      // 更新队列大小
    return true;
}

// 查看队头(Peek/Front)
bool peekFront(Queue *queue, int *value) {
    if (queue->front == NULL) {
        return false; // 队列为空,无法查看队头
    }
    *value = queue->front->data; // 返回队头元素
    return true;
}

// 查看队尾(Back)
bool peekBack(Queue *queue, int *value) {
    if (queue->rear == NULL) {
        return false; // 队列为空,无法查看队尾
    }
    *value = queue->rear->data; // 返回队尾元素
    return true;
}

// 检查队列是否为空(IsEmpty)
bool isEmpty(Queue *queue) {
    return queue->size == 0; // 如果大小为0,则队列为空
}

// 获取队列大小(Size)
int size(Queue *queue) {
    return queue->size; // 返回当前队列大小
}

// 测试链表实现的队列
int main() {
    Queue queue;
    init(&queue); // 初始化队列

    // 入队操作
    enqueue(&queue, 10);
    enqueue(&queue, 20);
    enqueue(&queue, 30);

    // 查看队头和队尾
    int value;
    if (peekFront(&queue, &value)) {
        printf("队头: %d\n", value);
    }
    if (peekBack(&queue, &value)) {
        printf("队尾: %d\n", value);
    }

    // 出队操作
    while (dequeue(&queue, &value)) {
        printf("出队: %d\n", value);
    }

    // 检查队列是否为空
    printf("队列是否为空: %s\n", isEmpty(&queue) ? 1 : 0);

    // 销毁队列
    destroy(&queue);
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值