队列的实现

772 阅读4分钟

「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战

💦 队列的概念及结构

相比栈,队列的特性和栈是相反的。
它只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO (First In First Out) 的特性。
入队列:进行插入操作的一端称为队尾;出队列:进行删除操作的一端称为队头
对于队列来说,一种入队顺序,只有一种出队顺序
    

在这里插入图片描述

队列的拓展:

实际中我们有时还会使用一种队列叫循环队列。如操作系统课程讲解生产者消费者模型时可以使用循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

在这里插入图片描述

为了能使用Q.rear == Q.front 来区别是队空还是队满,我们常常认为出现左图时的情况即为队空的情况,此时: rear == front;而右图的情况即为队满的情况,此时:rear + 1 == front

在这里插入图片描述

关于环形队列在下面的的栈和队列面试题中会讲到

队列的应用:

1️⃣ 实际中要保证公平排队的地方可以用它

2️⃣ 广度优先遍历

💦 队列的实现

这里对于队列的实现我们使用链表的方式 在这里插入图片描述

1.初始化

函数原型

在这里插入图片描述

函数实现

void QueueInit(Queue* pq)
{
    assert(pq);
    //把2个指针置空
    pq->phead = pq->ptail = NULL;
}
2.插入

函数原型

在这里插入图片描述

函数实现

void QueuePush(Queue* pq, QDataType x)
{
    assert(pq);
    //malloc空间,如果需要频繁的开辟空间建议再实现一个BuyQueueNode用于malloc
    QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
    if (newnode == NULL)
    {
	printf("malloc fail\n");
	exit(-1);
    }
    newnode->data = x;
    newnode->next = NULL;
    //第一次插入
    if (pq->phead == NULL)
    {
	pq->phead = pq->ptail = newnode;
    }
    //非第一次插入
    else
    {
	pq->ptail->next = newnode;
	pq->ptail = newnode;
    }
}
3.判空

函数原型

在这里插入图片描述

函数实现

bool QueueEmpty(Queue* pq)
{
	assert(pq);
	//空链表返回true,非空链表返回false
	return pq->phead == NULL;
}
4.删除

函数原型 在这里插入图片描述

函数实现

void QueuePop(Queue* pq)
{
    assert(pq);
    //链表为空时不能删除
    assert(!QueueEmpty(pq));
    //只有一个节点的情况
    if (pq->phead->next == NULL)
    {
	free(pq->phead);
	pq->phead = pq->ptail = NULL;
    }
    //多个节点的情况
    else
    {
	QueueNode* next = pq->phead->next;
	free(pq->phead)	;
	pq->phead = next;
    }
}
5.长度

函数原型

在这里插入图片描述

函数实现

QDataType QueueSize(Queue* pq)
{
    assert(pq);
    //如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度
    int sz = 0;
    QueueNode* cur = pq->phead;
    while (cur)
    {
	sz++;
	cur = cur->next;
    }
    return sz;
}
6.取头

函数原型

在这里插入图片描述

函数实现

QDataType QueueFront(Queue* pq)
{
    assert(pq);
    //链表为空时不能取头
    assert(!QueueEmpty(pq));

    return pq->phead->data;
}
7.取尾

函数原型

在这里插入图片描述

函数实现

QDataType QueueBack(Queue* pq)
{
    assert(pq);
    //链表为空时不能取尾
    assert(!QueueEmpty(pq));

    return pq->ptail->data;
}
8.销毁

函数原型

在这里插入图片描述

函数实现

void QueueDestory(Queue* pq)
{
    assert(pq);
	
    QueueNode* cur = pq->phead;
    //遍历链表
    while (cur)
    {
	QueueNode* next = cur->next;
	free(cur);
	cur = next;
    }
    pq->phead = pq->ptail = NULL;
}

💦 完整代码

这里需要三个文件

1️⃣ Queue.h,用于函数的声明

2️⃣ Queue.c,用于函数的定义

3️⃣ Test.c,用于测试函数


🧿 Queue.h
#pragma once

//头
#include<stdio.h>
#include<assert.h>
#include<stdbool.h>
#include<stdlib.h>

//结构体
typedef int QDataType;
typedef struct QueueNode
{
    struct QueueNode* next; //指向下一个节点
    QDataType data; //存储整型数据
}QueueNode;

typedef struct Queue
{
    QueueNode* phead;//头指针
    QueueNode* ptail;//尾指针
}Queue;

//函数
void QueueInit(Queue* pq); 
void QueuePush(Queue* pq, QDataType x);
bool QueueEmpty(Queue* pq);
void QueuePop(Queue* pq);
QDataType QueueSize(Queue* pq);
QDataType QueueFront(Queue* pq);
QDataType QueueBack(Queue* pq);
void QueueDestory(Queue* pq);
🧿 Queue.c
#include"Queue.h"

void QueueInit(Queue* pq)
{
    assert(pq);
    //把2个指针置空
    pq->phead = pq->ptail = NULL;
}
void QueuePush(Queue* pq, QDataType x)
{
    assert(pq);
    //malloc空间,如果需要频繁的开辟空间建议再实现一个BuyQueueNode用于malloc
    QueueNode* newnode = (QueueNode*)malloc(sizeof(QueueNode));
    if (newnode == NULL)
    {
	printf("malloc fail\n");
	exit(-1);
    }
    newnode->data = x;
    newnode->next = NULL;
    //第一次插入
    if (pq->phead == NULL)
    {
	pq->phead = pq->ptail = newnode;
    }
    //非第一次插入
    else
    {
	pq->ptail->next = newnode;
        pq->ptail = newnode;
    }
}
bool QueueEmpty(Queue* pq)
{
    assert(pq);
    //空链表返回true,非空链表返回false
    return pq->phead == NULL;
}
void QueuePop(Queue* pq)
{
    assert(pq);
    //链表为空时不能删除
    assert(!QueueEmpty(pq));
    //只有一个节点的情况
    if (pq->phead->next == NULL)
    {
	free(pq->phead);
	pq->phead = pq->ptail = NULL;
    }
    //多个节点的情况
    else
    {
	QueueNode* next = pq->phead->next;
	free(pq->phead)	;
	pq->phead = next;
    }
}
QDataType QueueSize(Queue* pq)
{
    assert(pq);
    //如果需要频繁的调用QueueSize这个接口,可以在Queue这个结构体中增加一个成员用于记录长度
    int sz = 0;
    QueueNode* cur = pq->phead;
    while (cur)
    {
	sz++;
	cur = cur->next;
    }
    return sz;
}
QDataType QueueFront(Queue* pq)
{
    assert(pq);
    //链表为空时不能取头
    assert(!QueueEmpty(pq));
    
    return pq->phead->data;
}
QDataType QueueBack(Queue* pq)
{
    assert(pq);
    //链表为空时不能取尾
    assert(!QueueEmpty(pq));

    return pq->ptail->data;
}
void QueueDestory(Queue* pq)
{
    assert(pq);
	
    QueueNode* cur = pq->phead;
    //遍历链表
    while (cur)
    {
	QueueNode* next = cur->next;
	free(cur);
	cur = next;
    }
    pq->phead = pq->ptail = NULL;
}
🧿 Test.c
#include"Queue.h"

int main()
{
    //1、传二级指针
    //2、返回值
    //3、带哨兵位的头节点
    //4、嵌套结构体 (这里使用这种方式)
    QueueNode q;
    //初始化
    QueueInit(&q);
    //插入
    QueuePush(&q, 1);
    QueuePush(&q, 2);
    QueuePush(&q, 3);
    QueuePush(&q, 4);
    //删除
    QueuePop(&q);
    QueuePop(&q);
    //取头
    QueueFront(&q);
    //取尾
    QueueBack(&q);
    //释放
    QueueDestory(&q);

    return 0;
}