• 【数据结构】队列、环形队列



    目录

    1.队列的概念及结构

     2.队列的实现

    3.队列的相关实现函数与源代码

    3.1初始化队列

    3.2 队尾入队列 

    3.3 队头出队列 

    3.4获取队列头部元素 

    3.5 获取队列队尾元素 

    3.6 获取队列中有效元素个数 

    3.7检测队列是否为空

    3.8销毁队列 

    4.环形队列

    4.1环形队列概念

    4.2实现环形队列

    4.2.1.实现环形队列的前期准备:相关结构体

    4.2.2.创建环形队列,设置队列长度为 k 

    4.2.3. 实现循环队列判空函数和判满函数

    4.2.5.插入和删除元素 

    4.2.6.从队首和队尾获取元素

    4.3环形队列完整代码

    在最后


    1.队列的概念及结构

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

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

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


    2.队列的实现

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


    3.队列的相关实现函数与源代码

    对其使用的结构体的定义:

    1. typedef int QDataType;
    2. //用单链表实现队列
    3. // 链式结构:表示队列
    4. typedef struct QListNode
    5. {
    6. struct QListNode* next;
    7. QDataType data;
    8. }QNode;
    9. // 队列的结构
    10. typedef struct Queue
    11. {
    12. QNode* front;
    13. QNode* rear;
    14. }Queue;

    通过队头队尾front、rear这两个指针来访问队列,执行队列的基本操作。


    3.1初始化队列

    1. // 初始化队列
    2. void QueueInit(Queue* q)
    3. {
    4. assert(q);
    5. q->front =q->rear= NULL;
    6. }

    3.2 队尾入队列 

    分两种情况队列为空和队列不为空。

    在创建新节点的时候直接把新节点的next初始化为空,后续链接就不用重新把尾结点置为空

    1. QNode* AddNode(QDataType data)
    2. {
    3. QNode* p = (QNode*)malloc(sizeof(QNode));
    4. if (p == NULL)
    5. {
    6. perror("malloc error\n");
    7. exit(-1);
    8. }
    9. p->data = data;
    10. p->next = NULL;//直接把新节点的next初始化为空,后续链接就不用重新把尾结点置为空
    11. return p;
    12. }
    13. //从队尾添加数据
    14. // 队尾入队列
    15. void QueuePush(Queue* q, QDataType data)
    16. {
    17. assert(q);
    18. //情况1.如果是空队列
    19. if (q->front == NULL)
    20. {
    21. q->front = q->rear = AddNode(data);
    22. }
    23. //情况2.队列不为空
    24. else
    25. {
    26. QNode* tail = AddNode(data);
    27. q->rear->next = tail;
    28. q->rear = tail;
    29. }
    30. }

    3.3 队头出队列 

    删除掉队头的数据

    请注意如果队列中只有一个元素时,需要进行判断。

    释放这个节点之后把指向队列的头尾指针都置为空,否则只是单纯的q->front=q->front->next,q->rear不变的话,就会造成q->rear指向一个已经释放的节点的情况,会造成野指针的问题。

    1. // 队头出队列
    2. void QueuePop(Queue* q)
    3. {
    4. assert(q);
    5. assert(!QueueEmpty(q));
    6. //如果删除的是最后一个
    7. if (q->front->next == NULL)
    8. {
    9. free(q->front);
    10. q->front = q->rear = NULL;
    11. }
    12. QNode* head = q->front;
    13. q->front = q->front->next;
    14. free(head);
    15. head = NULL;
    16. }

    3.4获取队列头部元素 

    1. // 获取队列头部元素
    2. QDataType QueueFront(Queue* q)
    3. {
    4. assert(q);
    5. assert(!QueueEmpty(q));
    6. return q->front->data;
    7. }

    3.5 获取队列队尾元素 

    1. // 获取队列队尾元素
    2. QDataType QueueBack(Queue* q)
    3. {
    4. assert(q);
    5. assert(!QueueEmpty(q));
    6. return q->rear->data;
    7. }

    3.6 获取队列中有效元素个数 

    遍历整个队列计算长度。

    1. // 获取队列中有效元素个数
    2. int QueueSize(Queue* q)
    3. {
    4. assert(q);
    5. assert(!QueueEmpty(q));
    6. int i = 0;
    7. QNode* head = q->front;
    8. while (head)
    9. {
    10. i++;
    11. head = head->next;
    12. }
    13. return i;
    14. }

    也可以直接在一开始定义的时候直接把结构体改为:

    1. // 队列的结构
    2. typedef struct Queue
    3. {
    4. QNode* front;
    5. QNode* rear;
    6. int size;
    7. }Queue;

    在之后的入队列出队列的时候都进行相应的++或者--


    3.7检测队列是否为空

    如果为空返回非零结果,如果非空返回0 

    1. // 检测队列是否为空,如果为空返回非零结果,如果非空返回0
    2. bool QueueEmpty(Queue* q)
    3. {
    4. assert(q);
    5. return q->front == NULL;
    6. }

    3.8销毁队列 

    记得销毁队列时,把队列的节点全部释放之后,记得在最后把指向队列的前后指针置为空。

    1. // 销毁队列
    2. void QueueDestroy(Queue* q)
    3. {
    4. assert(q);
    5. QNode* head = q->front;
    6. QNode* next = NULL;
    7. while (head)
    8. {
    9. next = head->next;
    10. free(head);
    11. head=next;
    12. }
    13. q->front = q->rear = NULL;//销毁队列之后记得把头尾指针置为空
    14. }


    4.环形队列

    4.1环形队列概念

    环形队列是一种线性数据结构,其操作表现基于 FIFO(先进先出)原则并且队尾被连接在队首之后以形成一个循环。它也被称为“环形缓冲器”。

    环形队列的一个好处是我们可以利用这个队列之前用过的空间。在一个普通队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间。但是使用循环队列,我们能使用这些空间去存储新的值。

     环形队列可以使用数组实现,也可以使用循环链表实现。如果使用数组实现,注意数组下标访问越界问题。

    以下用数组实现环形队列:


    4.2实现环形队列

    以力扣622.设计循环队列为例:

    622. 设计循环队列 - 力扣(LeetCode)https://leetcode.cn/problems/design-circular-queue/在这个题目中我们一共要实现有关环形队列的七个函数:

    MyCircularQueue(k): 构造器,设置队列长度为 k 。
    Front: 从队首获取元素。如果队列为空,返回 -1 。
    Rear: 获取队尾元素。如果队列为空,返回 -1 。
    enQueue(value): 向循环队列插入一个元素。如果成功插入则返回真。
    deQueue(): 从循环队列中删除一个元素。如果成功删除则返回真。
    isEmpty(): 检查循环队列是否为空。
    isFull(): 检查循环队列是否已满。


    4.2.1.实现环形队列的前期准备:相关结构体

    在这个结构体中需要定义指向环形队列起始位置的指针、头尾下标以及申请的实际空间的大小。

    1. typedef int QueueDataType;
    2. typedef struct {
    3. QueueDataType* a;//指向队列的指针
    4. QueueDataType front;//头尾下标
    5. QueueDataType tail;
    6. int size;//申请的实际空间大小
    7. } MyCircularQueue;

    4.2.2.创建环形队列,设置队列长度为 k 


    4.2.3. 实现循环队列判空函数和判满函数


    4.2.5.插入和删除元素 


    4.2.6.从队首和队尾获取元素


    4.3环形队列完整代码

    1. typedef int QueueDataType;
    2. typedef struct {
    3. QueueDataType* a;//指向队列的指针
    4. QueueDataType front;//头尾下标
    5. QueueDataType tail;
    6. int size;//申请的实际空间大小
    7. } MyCircularQueue;
    8. MyCircularQueue* myCircularQueueCreate(int k) {
    9. MyCircularQueue* point=(MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    10. //QueueDataType*
    11. point->a=(QueueDataType*)malloc(sizeof(QueueDataType)*(k+1));//多申请一个空余空间方便判空和满
    12. point->front=point->tail=0;//头尾下标都为0
    13. point->size=k+1;
    14. return point;
    15. }
    16. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    17. return obj->tail==obj->front;
    18. }
    19. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    20. return (obj->tail+1)%obj->size==obj->front;
    21. }
    22. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)
    23. {
    24. //先判断是否非满
    25. if(myCircularQueueIsFull(obj))
    26. {
    27. return false;
    28. }
    29. //向tail插入数据然后tail++
    30. obj->a[obj->tail]=value;
    31. //小心tail越界问题
    32. obj->tail++;
    33. obj->tail=obj->tail%obj->size;
    34. return true;
    35. }
    36. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    37. if(myCircularQueueIsEmpty(obj))
    38. {
    39. return false;
    40. }
    41. obj->front=(obj->front+1)%obj->size;
    42. return true;
    43. }
    44. int myCircularQueueFront(MyCircularQueue* obj)
    45. {
    46. if(myCircularQueueIsEmpty(obj))
    47. {
    48. return -1;
    49. }
    50. return obj->a[obj->front];
    51. }
    52. int myCircularQueueRear(MyCircularQueue* obj) {
    53. if(myCircularQueueIsEmpty(obj))
    54. {
    55. return -1;
    56. }
    57. //注意obj->tail始终在队列元素的下一个坐标,所以打印最后一个坐标要--
    58. return obj->a[(obj->tail-1+obj->size)%obj->size];
    59. }
    60. void myCircularQueueFree(MyCircularQueue* obj) {
    61. free(obj->a);
    62. free(obj);
    63. obj=NULL;
    64. }

    在最后

           好久不见,这里是媛仔~马上开学了,最近不知道为什么就开始摆烂了,好久也没有更新博客了T-T,把这篇博客当中重新开始吧!!加油加油!!

           希望这篇博客对你能够有所帮助,也希望大家可以和媛仔多多交流,谢谢大家!(最后的这个表情包是媛仔自己做的哦,祝你天天开心 ^-^

  • 相关阅读:
    【C语言】指针和数组笔试题解析(2)
    Android插件化学习之加载插件资源
    Linux命令(91)之mv
    107. 如何使用Docker以及Docker Compose部署Go Web应用
    FirmAFL
    2023届双非硕士四个月秋招历程总结
    JavaIO详解(磁盘操作、字节操作、字符操作、对象操作、网络操作、NIO)
    Java ZGC 算法调优
    Pick-a-Pic:An open dataset of user preferences for text-to-image generation
    关于电商API接口接入|接口请求重试的8种方法,你用哪种?
  • 原文地址:https://blog.csdn.net/vpurple_/article/details/126200057