• 【数据结构与算法】队列的实现


    🌠作者:@阿亮joy.
    🎆专栏:《数据结构与算法要啸着学》
    🎇座右铭:每个优秀的人都有一段沉默的时光,那段时光是付出了很多努力却得不到结果的日子,我们把它叫做扎根
    在这里插入图片描述


    👉队列的概念及结构👈

    队列:只允许在一端进行插入数据操作,在另一端进行删除数据操作的特殊线性表,队列具有先进先出FIFO(First In First Out)的原则

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

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

    在这里插入图片描述

    队列的结构在生活中非常地常见,比如排队时的抽号机就是一个典型的队列结构。那队列如何实现呢?我们一起来看一下。

    👉队列的实现👈

    队列也可以数组和链表的结构实现,使用链表的结构实现更优一些。因为如果使用数组的结构,出队列在数组头上出数据,需要挪动数据,时间复杂度为O(N),效率会比较低。

    在这里插入图片描述

    Queue.h

    #pragma once
    #include 
    #include 
    #include 
    #include 
    
    typedef int QDataType;
    typedef struct QueueNode
    {
    	QDataType data;
    	struct QueueNode* next;
    }QNode;
    
    typedef struct Queue
    {
    	QNode* head; // 头指针
    	QNode* tail; // 尾指针
    	int size;    // 节点的个数
    }Queue;
    
    void QueueInit(Queue* pq);
    void QueueDestroy(Queue* pq);
    void QueuePush(Queue* pq, QDataType x);
    void QueuePop(Queue* pq);
    QDataType QueueFront(Queue* pq);
    QDataType QueueBack(Queue* pq);
    bool QueueEmpty(Queue* pq);
    int QueueSize(Queue* pq);
    
    • 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

    队列要实现的函数接口有:初始化队列、销毁队列、数据入队、数据出队、返回队头的数据、返回队尾的数据、判断队列是否为空以及队列中数据的个数。这些接口实现起来也不是很难,我们一起来看一下。

    Queue.c

    #include "Queue.h"
    
    void QueueInit(Queue* pq)
    {
    	assert(pq);
    	pq->head = pq->tail = NULL;
    	pq->size = 0;
    }
    
    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;
    	pq->size = 0;
    }
    
    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++;
    }
    
    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--;
    }
    
    QDataType QueueFront(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->head->data;
    }
    
    QDataType QueueBack(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->tail->data;
    }
    
    bool QueueEmpty(Queue* pq)
    {
    	assert(pq);
    
    	return pq->size == 0;
    	//return pq->head == NULL && pq->tail == NULL;
    }
    int QueueSize(Queue* pq)
    {
    	assert(pq);
    
    	return 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
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105

    初始化队列

    头指针和尾指针都指向空,队列元素个数初始化为0

    void QueueInit(Queue* pq)
    {
    	assert(pq);
    	pq->head = pq->tail = NULL;
    	pq->size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    销毁队列

    利用while循环将申请的节点一一释放掉,然后头指针pq->head和尾指针pq->tail指向空,栈的数据个数置为 0pq->size = 0

    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;
    	pq->size = 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    数据入队

    1.申请新的节点newnode newnode->data = x,newnode->next = NULL
    2.数据入队:当pq->tail == NULL时,队列中没有节点,那么头指针和尾指针都赋值为newnode pq->head = pq->tail = newnode;当pq->tail != NULL时,队列中有节点,那么尾部链接上新节点newnode,然后newnode成为新的尾结点。
    3.队列数据个数加一pq->size++

    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

    数据出队

    1.判断队列是否为空
    2.数据出队:当pq->head->next == NULL时,队列中只有一个节点,释放该节点,头指针和尾指针都指向空;当pq->head->next != NULL时,释放让头指针指向当前节点的下一个节点,释放原来的头节点
    3.队列数据个数减一pq->size--

    在这里插入图片描述

    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

    返回队头数据

    先判断队列是否为空,不为空时,返回队头数据。

    QDataType QueueFront(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->head->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    返回队尾数据

    先判断队列是否为空,不为空时,返回队尾数据。

    QDataType QueueFront(Queue* pq)
    {
    	assert(pq);
    	assert(!QueueEmpty(pq));
    
    	return pq->tail->data;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    判断队列是否为空

    判断队列是否为空有两种方式:1.判断pq->size等不等于 0;2.判断头指针pq->head和尾指针pq->tail是否等于空指针NULL

    bool QueueEmpty(Queue* pq)
    {
    	assert(pq);
    
    	return pq->size == 0;
    	//return pq->head == NULL && pq->tail == NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    队列中数据的个数

    直接返回队列数据的个数pq->size

    int QueueSize(Queue* pq)
    {
    	assert(pq);
    
    	return pq->size;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    Test.c

    以下为测试函数接口的代码,大家可以参考一下。需要注意的是,打印队列中的数据是通过打印队头数据、Pop掉队头数据的方式来实现的。

    #include "Queue.h"
    
    void QueueTest()
    {
    	Queue q;
    	QueueInit(&q);
    	QueuePush(&q, 1);
    	QueuePush(&q, 2);
    	QueuePush(&q, 3);
    
    	printf("%d ", QueueFront(&q));
    	QueuePop(&q);
    	printf("%d ", QueueFront(&q));
    	QueuePop(&q);
    
    	QueuePush(&q, 4);
    	QueuePush(&q, 5);
    	QueuePush(&q, 6);
    
    	while (!QueueEmpty(&q))
    	{
    		printf("%d ", QueueFront(&q));
    		QueuePop(&q);
    	}
    	printf("\n");
    
    	QueueDestroy(&q);
    }
    
    int main()
    {
    	QueueTest();
    
    	return 0;
    }
    
    • 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
    • 31
    • 32
    • 33
    • 34
    • 35

    在这里插入图片描述

    👉总结👈

    本篇博客主要讲解了队列的实现,最重要的函数接口是数据入队和数据出队。在下一篇博客,本人将给大家带来几道 OJ 题,大家可以期待一下。以上就是本篇博客的全部内容了,如果大家觉得有收获的话,可以点个三连支持一下!谢谢大家啦!💖💝❣️

  • 相关阅读:
    痛苦皆来自于认知的局限
    2.1-梯度下降
    (52)性能分析 ---CPU的性能分析
    系列文章|云原生时代下微服务架构进阶之路 - Spring Boot
    Redis学习笔记13:基于spring data redis及lua脚本list列表实现环形结构案例
    3、核心配置文件
    如何使用adb command来设置cpu频率和核数
    Java多线程wait()和notify()方法图解
    分布式技术材料整理
    如何弄懂复杂项目
  • 原文地址:https://blog.csdn.net/m0_63639164/article/details/127328375