数据结构-队列 c语言使用链表和数组分别实现

队列

定义

队列(queue)是一种遵循先入后到规则的线性数据结构,将队列头部称为“队首”,尾部称为“队尾”,把元素加入队尾称为“入队”,删除队首元素称为“出队”。

队列实现

基于链表的实现

将链表的头节点和尾结点分别视为“队首”和“队尾”,规定队尾仅可添加节点,队首仅可删除节点。

/* 基于链表实现的队列 */
typedef struct
{
    ListNode *front, *rear;
    int queSize;
} LinkedListQueue;

/* 构造函数 */
LinkedListQueue *newLinkedListQueue()
{
    LinkedListQueue *queue = (LinkedListQueue *)malloc(sizeof(LinkedListQueue));
    queue->front = NULL;
    queue->rear = NULL;
    queue->queSize = 0;
    return queue;
}

/* 析构函数 */
void delLinkedListQueue(LinkedListQueue *queue)
{
    // 释放所有节点
    while (queue->front != NULL)
    {
        ListNode *tmp = queue->front;
        queue->front = queue->front->next;
        free(tmp);
    }
    // 释放 queue 结构体
    free(queue);
}

/* 获取队列的长度 */
int size(LinkedListQueue *queue)
{
    return queue->queSize;
}

/* 判断队列是否为空 */
bool empty(LinkedListQueue *queue)
{
    return (size(queue) == 0);
}

/* 入队 */
void push(LinkedListQueue *queue, int num)
{
    // 尾节点处添加 node
    ListNode *node = newListNode(num);
    // 如果队列为空,则令头、尾节点都指向该节点
    if (queue->front == NULL)
    {
        queue->front = node;
        queue->rear = node;
    }
    // 如果队列不为空,则将该节点添加到尾节点后
    else
    {
        queue->rear->next = node;
        queue->rear = node;
    }
    queue->queSize++;
}

/* 访问队首元素 */
int peek(LinkedListQueue *queue)
{
    assert(size(queue) && queue->front); // 队列为空时,不能访问队首元素,assert是宏,用于断言,断言失败会终止程序
    return queue->front->val;
}

/* 出队 */
int pop(LinkedListQueue *queue)
{
    int num = peek(queue);
    ListNode *tmp = queue->front;
    queue->front = queue->front->next;
    free(tmp);
    queue->queSize--;
    return num;
}

/* 打印队列 */
void printLinkedListQueue(LinkedListQueue *queue)
{
    int *arr = malloc(sizeof(int) * queue->queSize);
    // 拷贝链表中的数据到数组
    int i;
    ListNode *node;
    for (i = 0, node = queue->front; i < queue->queSize; i++)
    {
        arr[i] = node->val;
        node = node->next;
    }
    printArray(arr, queue->queSize);
    free(arr);
}

基于数组实现

在数组中删除首元素的时间复杂度为O(N),导致出队效率低,可以用一个巧妙方法避免这个问题。

使用一个变量front指向队首元素的索引,并维护一个变量size用于记录队列长度,则定义rear=front+size,计算出rear指向队尾元素的下个位置,则数组中包含元素的有效区间为[front,rear-1],这时,出入队操作只需进行一次操作,时间复杂度均为O(1).

有一个问题:在不断入队和出队的过程中,front和rear都在向右移动,当他们到达数组尾部就无法继续移动了,为了解决这个问题,可以将数组视为首尾相接的“环形数组”,

实现思路:我们需要将front或rear再越过数组尾部时,直接回到数组头部继续遍历,这种周期性规律可以通过“取余操作”来实现

/* 基于环形数组实现的队列 */
typedef struct
{
    int *nums;       // 用于存储队列元素的数组
    int front;       // 队首指针,指向队首元素
    int queSize;     // 队列长度,即队列中元素的个数
    int queCapacity; // 队列容量,即数组长度
} ArrayQueue;

/* 构造函数 */
ArrayQueue *newArrayQueue(int capacity)
{
    ArrayQueue *queue = (ArrayQueue *)malloc(sizeof(ArrayQueue));
    // 初始化数组
    queue->queCapacity = capacity;
    queue->nums = (int *)malloc(sizeof(int) * queue->queCapacity);
    queue->front = queue->queSize = 0;
    return queue;
}

/* 析构函数 */
void delArrayQueue(ArrayQueue *queue)
{
    free(queue->nums);
    free(queue);
}

/* 获取队列的容量 */
int capacity(ArrayQueue *queue)
{
    return queue->queCapacity;
}

/* 获取队列的长度 */
int size(ArrayQueue *queue)
{
    return queue->queSize;
}

/* 判断队列是否为空 */
bool empty(ArrayQueue *queue)
{
    return queue->queSize == 0;
}

/* 访问队首元素 */
int peek(ArrayQueue *queue)
{
    assert(size(queue) != 0);
    return queue->nums[queue->front];
}

/* 入队 */
void push(ArrayQueue *queue, int num)
{
    if (size(queue) == capacity(queue))
    {
        printf("队列已满\r\n");
        return;
    }
    // 计算队尾指针,指向队尾索引 + 1
    // 通过取余操作实现 rear 越过数组尾部后回到头部
    int rear = (queue->front + queue->queSize) % queue->queCapacity;
    // 将 num 添加至队尾
    queue->nums[rear] = num;
    queue->queSize++;
}

/* 出队 */
int pop(ArrayQueue *queue)
{
    int num = peek(queue);
    // 队首指针向后移动一位,若越过尾部,则返回到数组头部
    queue->front = (queue->front + 1) % queue->queCapacity;
    queue->queSize--;
    return num;
}

典型应用

  • 淘宝订单
  • 代办事项:先来后到功能
posted @   无境之路  阅读(34)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律

阅读目录(Content)

此页目录为空

点击右上角即可分享
微信分享提示