队列详解包含测试过的完整代码

264 阅读5分钟

星辰运算者: 一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情

零.前言

栈和队列都是顺序表或者链表的特殊结构,栈由于是尾插尾删,所以我们使用顺序表来实现;而队列使用尾插头删,如果我们使用顺序表的话则正中其缺点,即插入删除数据的时候需要进行数据的挪动,所以我们使用链表来进行队列的实现。

1.队列的结构

1.逻辑结构

在这里插入图片描述 即先进先出,头删尾插。

物理结构

物理结构与链表是相同的,这里就不画了。

2.实现队列的基本操作

1.队列的建立

typedef struct QueueNode {
    struct QueueNode* next;
    DataType x;
}QNode;
typedef struct Queue
{
    QNode* head;
    QNode* tail;
}Queue;

这里使用了两个结构体来进行队列的实现,第一个结构体表示队列的一个节点,第二个结构体表示一个队列的头指针和尾指针。第二个结构体用头指针和尾指针抽象表示一个队列。

2.队列的初始化

void QueueInit(Queue* pq)
{
    assert(pq);
    pq->head = NULL;
    pq->tail = NULL;//将pq队列的head和tail都置为空
}

建立一个队列pq,将它的头指针和尾指针都指向空。

3.队列的销毁

void QueueDestroy(Queue* pq)
{
    assert(pq);
    QNode* cur = pq->head;
    while (cur)
    {
        QNode* next = cur->next;
        free(cur);
        cur = next;//遍历队列,由于释放cur之后无法再找到cur->next了,所以定义一个next先存起来
    }
    pq->head = pq->tail = NULL;
}

和链表一样,在进行队列的销毁时,也需要遍历整个序列,然后依次销毁空间,最后指针别忘记指空。

4.判断队列是否为空

bool QueueEmpty(Queue* pq)
{
    assert(pq);
    return pq->head == NULL;//当head为空则为空返回TRUE
}

我们用bool类型来接收结果,如果为空返回True,不为空返回False。

5.计算队列的大小

int QueueSize(Queue* pq)
{
    assert(pq);
    int n = 0;
    QNode* cur = pq->head;
    while (cur)
    {
        n++;
        cur = cur->next;//利用cur进行遍历
    }
    return n;
}

遍历队列,只到找到空指针,每一次对n加一。

3.队列的插入与删除

1.队列的插入

void QueuePush(Queue* pq, DataType x)
{
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    newnode->x = x;
    newnode->next = NULL;//初始化一个节点
    if (pq->head == NULL)
    {
        pq->head =pq->tail= newnode;//当只有一个节点的时候将head和tail赋值为newnode
    }
    else
    {
        pq->tail->next = newnode;
        pq->tail = newnode;//将newnode作为新的尾指针
    }
}

队列采用的是尾插,即在tail后插入,如果队列为空,则插入的元素就为头元素,若不为空则在尾部插入,然后将节点作为新的尾。

2.队列的删除

void QueuePop(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    Queue* next = pq->head->next;
    free(pq->head);
    pq->head = next;//首先定义一个next接收head->next
    if (pq->head == NULL)
    {
        pq->tail = NULL;
    }//当只有一个元素时删除后要将pq的尾指针赋值为空
}

对队列进行删除,需要先断言判断队列是否为空,不为空则从头部开始删除,然后将第二个元素作为头部。

4.得到队列的头尾

1.得到队列的头

int QueueFront(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    return pq->head->x;
}

2.得到队列的尾

int QueueBack(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    return pq->tail->x;
}

5.全部文件

1.queue.h文件

#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<stdbool.h>
#define DataType int
typedef struct QueueNode {
    struct QueueNode* next;
    DataType x;
}QNode;
typedef struct Queue
{
    QNode* head;
    QNode* tail;
}Queue;
void QueueInit(Queue* pq);
void QueueDestroy(Queue* pq);
void QueuePush(Queue* pq, DataType x);
void QueuePop(Queue* pq);
bool QueueEmpty(Queue* pq);
int QueueSize(Queue* pq);
int QueueFront(Queue* pq);
int QueueBack(Queue* pq);
void QueuePrint(Queue* pq);

2.queue.c

#define _CRT_SECURE_NO_WARNINGS 1
#include"queue.h"
void QueueInit(Queue* pq)
{
    assert(pq);
    pq->head = NULL;
    pq->tail = NULL;//将pq队列的head和tail都置为空
}
void QueueDestroy(Queue* pq)
{
    assert(pq);
    QNode* cur = pq->head;
    while (cur)
    {
        QNode* next = cur->next;
        free(cur);
        cur = next;//遍历队列,由于释放cur之后无法再找到cur->next了,所以定义一个next先存起来
    }
    pq->head = pq->tail = NULL;
}
void QueuePush(Queue* pq, DataType x)
{
    QNode* newnode = (QNode*)malloc(sizeof(QNode));
    newnode->x = x;
    newnode->next = NULL;//初始化一个节点
    if (pq->head == NULL)
    {
        pq->head =pq->tail= newnode;//当只有一个节点的时候将head和tail赋值为newnode
    }
    else
    {
        pq->tail->next = newnode;
        pq->tail = newnode;//将newnode作为新的尾指针
    }
}
void QueuePop(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    Queue* next = pq->head->next;
    free(pq->head);
    pq->head = next;//首先定义一个next接收head->next
    if (pq->head == NULL)
    {
        pq->tail = NULL;
    }//当只有一个元素时删除后要将pq的尾指针赋值为空
}
bool QueueEmpty(Queue* pq)
{
    assert(pq);
    return pq->head == NULL;//当head为空则为空返回TRUE
}
int QueueSize(Queue* pq)
{
    assert(pq);
    int n = 0;
    QNode* cur = pq->head;
    while (cur)
    {
        n++;
        cur = cur->next;//利用cur进行遍历
    }
    return n;
}
int QueueFront(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    return pq->head->x;
}
int QueueBack(Queue* pq)
{
    assert(pq);
    assert(pq->head);
    return pq->tail->x;
}
void QueuePrint(Queue* pq)
{
    assert(pq);
    QNode* cur = pq->head;
    while (cur)
    {
        printf("%d ", cur->x);
        cur = cur->next;
    }
    printf("\n");
}

3.test.c文件

#define _CRT_SECURE_NO_WARNINGS 1
#include"queue.h"
void menu()
{
    printf("****1.入队*******2.出队****\n");
    printf("****3.队首*******4.队尾****\n");
    printf("****5.是否为空***6.大小****\n");
    printf("****0.删除队列*************\n");
}
int main()
{
    Queue pq;
    QueueInit(&pq);
    QueuePush(&pq, 1);
    QueuePush(&pq, 2);
    QueuePush(&pq, 3);
    QueuePush(&pq, 4);
    QueuePush(&pq, 5);
    QueuePrint(&pq);
    //printf("write by lonely little boy\n");
    int input = 0;
    do{
        menu();
        int x;
        int y;
        scanf("%d", &input);
        switch (input)
        {
        case 1:scanf("%d", &x);
            QueuePush(&pq, x);
            QueuePrint(&pq);
            break;
        case 2:QueuePop(&pq);
            QueuePrint(&pq);
            break;
        case 3:y = QueueFront(&pq);
            printf("%d\n", y);
            break;
        case 4:y = QueueBack(&pq);
            printf("%d\n", y);
            break;
        case 5:y = QueueEmpty(&pq);
            if (y == 1)
            {
                printf("empty!\n");
            }
            else
            {
                printf("not empty\n");
            }
            break;
        case 6:y = QueueSize(&pq);
            printf("%d\n", y);
            break;
        case 0:QueueDestroy(&pq);
            break;
        default:printf("wrong type!\n");
            break;
        }
    } while (input);
    return 0;
}

6.测试

插入:成功 删除至空报错:成功 判断为空:成功 计算大小为空为0:成功 取头取尾空时报错:成功 退出:成功 综上代码测试成功

7.总结

队列是建立起来和别的不一样的存在,需要定义两个结构体,定义了头尾指针方便了我们进行对队列的操作,最近也做了一些栈和队列的题目,发现其实很多用C++编写会相对简单一些,C语言不是那么太适合刷题,但是要了解一些最最基本的结构实现的话,C语言是最好的选择,我认为原理还是最重要的内容,如果不了解原理是没有办法进行创新的。