• 数据结构之——OJ题环形队列实现详解


    目录

    数组版队列图:

    针对方法2:

    方法1.为数组预留一块空闲的空间

    方法2.使用size统计队列存入的个数


    之前我发了一篇关于队列的数据结构实现博客,有兴趣的伙伴们可以去看看:

     数据结构之——队列详解 ( 1 )_

            队列的原则是先进先出,后进后出,所以队列有两个指针:队头(head)和队尾( tail )指针,head 用于指向队列的头部位置并且插入元素,tail用于指向队列的尾部位置并删除元素,而接下来讲的循环队列也是要遵循以上规则。

            队列是一条直线,循环队列就像一个圆一样,头尾相接:

            队列既可以用数组实现,也可以用链表实现,循环队列也是如此,而且循环队列的长度是固定的,那么它只支持一次性 的堆区空间开辟!大多数情况是使用数组实现,那么下面我来展示一下数组版的队列图解:

    数组版队列图:

     

     通过图解发现,每次添加数据后,tail就会指向队尾位置的下一个位置。

    当队列为空和队列为满时,head和tail指针都指向同一个位置。

             在图3中 head和tail指针又指向了同一个位置,此时数组为满,所以产生一个问题:当队列为空或者为满时,无法区分,头尾指针都指向同一块空间!那么该如何区分呢?

            其实有两种方法进行区分:

        

            方法1.为队列创建一个size,用于统计队列存入数据的个数,这样当size==0时,那么即使head和tail指向同一块空间,也为空;size==数组总长度时,head和tail指向同一块空间表示队列数据已满。

            方法2:在开辟数组的过程中,多预留一块空间,这块空间不需要存储数据,当尾指针tail指向的位置的下一个位置如果是队头指针head指向的位置,表明队列已满;若tail指向的位置的下一个位置不为head,则表明队列为空。

    针对方法2:

            例下图:当队列中存入了7个数据后,tail指向数据7的下一块空间,而方法2针对队列为空或满的方式为:tail位置的下一个位置为head指向的位置,表明已满,所以图1的队列(数组是满的) 

     

            而图2的队列经过删除数据1,2,3,且又添加数据9,10后,tail的位置的下一个位置并不是head指向的位置,所以该队列不满(数组允许存储的个数为7个,而只存储了6个)

    注:数组中始终有一块空间不允许存储值。 

     基于此,这两种方法均可以很好的用来分辨队列空或者满的情况。


     而今天要实现的是力扣的OJ题:设计循环队列。通过上述的讲解,我们可以轻松的实现该题

    方法1.为数组预留一块空闲的空间

    循环队列实现代码:

    1. //方法3.多预留一个空间时的取余方法
    2. //环形队列————长度是固定的,所以删除数据时不可以free掉那块空间
    3. typedef struct {
    4. int* arr;
    5. int head;
    6. int tail;
    7. int N; //N表示要为数组原有长度多预留一个空间
    8. } MyCircularQueue;
    9. MyCircularQueue* myCircularQueueCreate(int k) { //K就是原有的长度
    10. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    11. obj->arr = (int*)malloc(sizeof(int) * (k + 1));
    12. obj->head = obj->tail = 0;
    13. obj->N = k + 1;
    14. return obj;
    15. }
    16. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {//判断队列是否为空
    17. if (obj->head == obj->tail) //若头指针与尾指针指向的位置相同
    18. //(之前初始化的时候让头尾指针都指向数组首元素),说明为空
    19. return true;
    20. else //若两指针位置各不相同,说明队列不为空
    21. return false;
    22. }
    23. bool myCircularQueueIsFull(MyCircularQueue* obj) { //判断队列是否满了
    24. //若尾指针+1取余数组现有长度后仍指向队头指针,说明是满的
    25. //尾指针指向的位置的下一个位置如果等于头指针指向的位置,表明队列满了!!!
    26. if ((obj->tail + 1) % obj->N == obj->head) {
    27. return true;
    28. }
    29. //否则不满
    30. else
    31. return false;
    32. }
    33. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    34. //判断若队列若不满(不满包括队列为空和数据个数不超过数组长度k,
    35. //才可以插入元素value——队列只能尾插
    36. if (myCircularQueueIsFull(obj))
    37. return false; //队列满了则不允许再添加数据
    38. else {
    39. obj->arr[obj->tail] = value;
    40. obj->tail++;
    41. //若尾指针++到超过数组长度N时,需要尾指针回到数组开头往后加————
    42. //因为是环形队列
    43. obj->tail = obj->tail % (obj->N); //让尾指针回到开头的方法.取余
    44. return true;
    45. }
    46. }
    47. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    48. //判断若队列不为空(不空包括满和数据个数大于0)则可以删除元素——队列只能头删
    49. if (myCircularQueueIsEmpty(obj))
    50. return false;
    51. else {
    52. //头删后,头指针需要指向下一个数据,所以head++,只要跳过就行,
    53. //不用删除数据,因为新添加的数据会覆盖掉原有数据
    54. obj->head++;
    55. //若头指针++到超过数组长度N时,需要头指针回到数组开头往后加,因为是环形队列
    56. obj->head = obj->head % (obj->N);
    57. return true;
    58. }
    59. }
    60. int myCircularQueueFront(MyCircularQueue* obj) {
    61. if (myCircularQueueIsEmpty(obj)) {
    62. return -1;
    63. }
    64. else {
    65. return obj->arr[obj->head];
    66. }
    67. }
    68. int myCircularQueueRear(MyCircularQueue* obj) {
    69. if (myCircularQueueIsEmpty(obj)) {
    70. return -1;
    71. }
    72. else {
    73. //情况1:尾指针不处在边界位置时,非特殊情况,back-1
    74. //因为尾插后back所处位置有了新数据,back要指向下一个位置等待新数据的到来
    75. if (obj->tail != 0) {
    76. return obj->arr[obj->tail - 1];
    77. }
    78. else {
    79. //情况2:尾指针处在特殊情况时,即尾指针指向开头,
    80. //那要返回数组的最后一个元素,因为数
    81. //组的最后一个元素被添加后,back才来到了数组开头
    82. //所以应该返回数组的最后一个元素
    83. return obj->arr[obj->N - 1];//
    84. }
    85. }
    86. }
    87. //释放堆区空间
    88. void myCircularQueueFree(MyCircularQueue* obj) {
    89. free(obj->arr);
    90. free(obj);
    91. }

    方法2.使用size统计队列存入的个数

    1. //方法2.使用size统计
    2. typedef struct {
    3. //第一种实现:
    4. int* data;
    5. //第一个元素的位置: 队头位置
    6. int front;
    7. //最后一个元素的下一个位置
    8. int rear;
    9. //数组长度
    10. int k;
    11. //数组当前存储的个数
    12. int size;
    13. } MyCircularQueue;
    14. MyCircularQueue* myCircularQueueCreate(int k) {
    15. //多开一个元素的空间
    16. MyCircularQueue* mq = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
    17. //以下为结构体成员的初始化
    18. //为结构体指针中的成员变量开辟的空间
    19. mq->data = (int*)malloc(sizeof(int) * k);
    20. mq->front = mq->rear = 0;
    21. mq->k = k;
    22. mq->size = 0;
    23. return mq;
    24. }
    25. bool myCircularQueueIsEmpty(MyCircularQueue* obj) {
    26. if (obj->size == 0)
    27. return true;
    28. else
    29. return false;
    30. }
    31. bool myCircularQueueIsFull(MyCircularQueue* obj) {
    32. if (obj->size == obj->k)
    33. return true;
    34. else
    35. return false;
    36. }
    37. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) {
    38. //判断队列是否已满
    39. if (myCircularQueueIsFull(obj))
    40. return false;
    41. obj->data[obj->rear++] = value;
    42. //判断队尾是否越界
    43. if (obj->rear >= obj->k)
    44. obj->rear = 0;
    45. ++obj->size;
    46. return true;
    47. }
    48. bool myCircularQueueDeQueue(MyCircularQueue* obj) {
    49. //判断队列是否为空
    50. if (myCircularQueueIsEmpty(obj))
    51. return false;
    52. //出队
    53. obj->front++;
    54. //判断队头是否越界
    55. if (obj->front >= obj->k)
    56. obj->front = 0;
    57. --obj->size;
    58. return true;
    59. }
    60. int myCircularQueueFront(MyCircularQueue* obj) {
    61. if (myCircularQueueIsEmpty(obj))
    62. return -1;
    63. else
    64. return obj->data[obj->front];
    65. }
    66. int myCircularQueueRear(MyCircularQueue* obj) {
    67. if (myCircularQueueIsEmpty(obj))
    68. return -1;
    69. //返回队尾rear的前一个位置的元素
    70. //判断rear是否为0;
    71. if (obj->rear != 0)
    72. return obj->data[obj->rear - 1];
    73. else
    74. return obj->data[obj->k - 1];
    75. }
    76. void myCircularQueueFree(MyCircularQueue* obj) {
    77. free(obj->data);
    78. free(obj);
    79. }

  • 相关阅读:
    LLVM系列:1.设计思想和LLVM IR简介
    如何保护客户数据并降低合规风险
    软件测试基础-自动化测试技术
    xbox下载游戏速度一直为0b/s怎么办?
    大数据Hadoop入门之集群的搭建
    【设计模式】3种工厂模式
    【Android development】系列_01创建安卓应用程序
    win7打开文件夹总弹出新窗口怎么办
    【分享】高精度RTK定位解决方案
    Visual Studio 2022平台的使用
  • 原文地址:https://blog.csdn.net/weixin_69283129/article/details/126668493