• 【数据结构 C语言版】第五篇 队列(看完刷题无敌)


    【数据结构 C语言版】第五篇 队列(看完刷题无敌)

    写在前面

    更新情况记录:

    最近更新时间更新次数
    2022/10/191

    参考博客与书籍以及链接:
    (非常感谢这些博主们的文章,将我的一些疑问得到解决。)

    参考博客链接或书籍名称
    《数据结构》陈越
    代码随想录
    总目录:目前数据结构文章太少,没有写。

    再说一句:跟书上代码不一样,但是刷题很好刷


    正文

    0.前置内容

    如果你c语言还有困难的话,请看我的博客:

    【十万字+详解C/C++】C/C++目录(持续更新)_潮.eth的博客-CSDN博客

    关注我,不迷路。

    1.队列的概念

    在现实生活中,我们经常会遇到为了得到某种服务而排队的情况,比如,食堂买饭时需要排队,银行存款时也需要排队。在计算机资源管理中也有类似的情景,比如,计算机的CPU资源是有限的(早先计算机只有一个CPU),但同时有许多程序(进程)需要CPU来进行,这些准备运行的进程就需要排队。

    在许多应用中,排队的基本规则是:新来者排队在队伍末尾,排在队伍前面的人先得到服务,期间不允许插队。对于这类排队问题,需要有一种能解决共性问题的数据序列的管理组织方式,在这个方式中,多个数据构成一个有序序列,面对这个序列的操作(比如插入、删除)有一定的要求:

    只能在一端插入,而在另一端删除。这样的数据组织方式就是"队列"。

    队列:只允许在一端进行入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出(FIFO:first in first out)的特点。

    入队列:进行插入操作的一端称为队尾。

    出队列:进行删除操作的一端称为队头。

    image-20221019103439050

    2.队列的链式实现

    队列也可以数组和链表的结构实现,使用链表的结构实现更优一些,因为如果使用数组的结构,出队列在数组头上出数据,效率会比较低。

    2.1 队列的链式结构

    第一个结构体表示队列结点

    第二个结构体表示队列的头结点与尾结点,我们直接操作Queue结构体就行

    typedef int QDataType;
    typedef struct QueueNode
    {
    	struct QueueNode* next;
    	QDataType data;
    }QNode;
    
    typedef struct Queue
    {
    	QNode* head;
    	QNode* tail;
    	int size;//队列的最大容量
    }Queue;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.2 队列的接口

    // 初始化队列
    void QueueInit(Queue* pq); 
    // 队尾入队列
    void QueuePush(Queue* pq, QDataType x); 
    // 队头出队列
    void QueuePop(Queue* pq); 
    // 获取队列头部元素
    QDataType QueueFront(Queue* pq); 
    // 获取队列队尾元素
    QDataType QueueBack(Queue* pq); 
    // 获取队列中有效元素个数
    int QueueSize(Queue* pq); 
    // 检测队列是否为空,如果为空返回非零结果,如果非空返回0 
    int QueueEmpty(Queue* pq); 
    // 销毁队列
    void QueueDestroy(Queue* pq);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    2.3 队列接口的实现

    2.3.1 初始化队列
    void QueueInit(Queue* pq)
    {
    	assert(pq);
    	pq->head = pq->tail = NULL;
    	pq->size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.3.2 队尾入队列
    void QueuePush(Queue* pq, QDataType x)
    {
    	assert(pq);
        //开辟新节点
    	QNode* newnode = (QNode*)malloc(sizeof(QNode));
    	if (newnode == NULL)
    	{
    		perror("malloc fail");
    		exit(-1);
    	}
    	else
    	{
            //开辟没有问题就开始赋值。
    		newnode->data = x;
    		newnode->next = NULL;
    	}
        //如果队列为空
    	if (pq->tail == NULL)
    	{
    		pq->head = pq->tail = newnode;
    	}
        //如果队列不是空
    	else
    	{
    		pq->tail->next = newnode;
    		pq->tail = newnode;
    	}
        //队列容量加一
    	pq->size++;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    2.3.3 队头出队列
    void QueuePop(Queue* pq)
    {
    	assert(pq);
        //队列不能为空
    	assert(!QueueEmpty(pq));
        
        //如果队列只有一个节点
    	if (pq->head->next == NULL)
    	{
    		free(pq->head);
    		pq->head = pq->tail = NULL;
    	}
        //队列有多个节点就直接头删
    	else
    	{
    		QNode* del = pq->head;
    		pq->head = pq->head->next;
    
    		free(del);
    		del = NULL;
    	}
    
    	pq->size--;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    2.3.4 获取队列头部元素
    QDataType QueueFront(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->head->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.3.5 获取队列队尾元素
    QDataType QueueBack(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->tail->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    2.3.6 获取队列中有效元素个数
    int QueueSize(Queue* pq)
    {
    	assert(pq);
    	return pq->size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    如果你的Queue结构体中没有size,那么就是下面的样子:

    int QueueSize(Queue* pq)
    {
    	assert(pq);
    	QNode* cur = pq->head;
    	int n = 0;
    	while (cur)
    	{
    		++n;
    		cur = cur->next;
    	}
    	return n;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    2.3.7 检测队列是否为空
    bool QueueEmpty(Queue* pq)
    {
    	assert(pq);
    
    	return pq->head == NULL && pq->tail == NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    2.3.8 销毁队列
    void QueueDestroy(Queue* pq)
    {
    	assert(pq);
    	QNode* cur = pq->head;
    	while (cur)
    	{
    		QNode* del = cur;
    		cur = cur->next;
    		free(del);
    	}
    
    	pq->head = pq->tail = NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    3.循环队列

    实际中我们有时还会使用一种队列叫循环队列。环形队列可以使用数组实现,也可以使用循环链表实现。

    image-20221019153835795

    image-20221019153856336

    注意:我们一般会多开出一个空间,用于判断是否满,也就是rear+1==front。

    那么为什么要多出这个空间呢,假如没有的话,满的时候是,rear==front;

    空的时候也是rear==front;那么就无法判断是否满了还是空了。

    具体请看栈和队列的习题(待更新)。

    完毕。

  • 相关阅读:
    源码中的设计模式--模板方法模式
    《思科 - GNS3 - Pythonping》
    LeetCode每日一题(263. Ugly Number)
    30天刷题计划(四)
    Java_引用变量
    基础算法练习200题14、阶乘
    图论期末复习(《图论机器应用》——朴月华)
    spoof_call的分析与改进
    Java面试宝典(超级详细)
    【整数正序按指定位数分解为2个数】2023-9-19
  • 原文地址:https://blog.csdn.net/m0_54381284/article/details/127409548