当前位置:   article > 正文

DS:循环队列的实现

DS:循环队列的实现

                                                 创作不易,给个三连吧!! 

一、前言

对于循环队列,博主也是源自于一道力扣的OJ题

力扣:循环队列的设置

      后来我在网上查过,这个循环队列是有自己的应用场景的!!并不是出题者为了出题而产生的,所以我觉得不光要能做会这道题,还得多去探究这道题的不同方式。而且这道题虽然是循环队列,看似好像要把头和尾连起来,但实际上实现过程中是可以不需要的!这也是他非常特别的一点,因此在这我会重点介绍他的数组实现和链式结构实现。

二、数组实现循环队列

怎么用数组去实现循环队列呢?我们来画图研究一下:

2.1 结构体的创建

  1. typedef int QDataType;
  2. typedef struct MyCircularQueue
  3. {
  4. QDataType* a;//动态数组
  5. int capacity;//记录循环队列的容量
  6. int front;//记录队首
  7. int rear;//记录队尾
  8. } MyCircularQueue;

2.2 构造一个k长度的队列并返回

根据我们之前的思路,我们要多创建一块空间!!

  1. MyCircularQueue* myCircularQueueCreate(int k)
  2. {
  3. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  4. if (obj == NULL)
  5. {
  6. perror("malloc obj fail");
  7. exit(1);
  8. }
  9. obj->a = (QDataType*)malloc(sizeof(QDataType) * (k + 1));
  10. if (obj->a == NULL)
  11. {
  12. perror("malloc obj->a fail");
  13. exit(1);
  14. }
  15. obj->front = obj->rear = 0;
  16. obj->capacity = k;
  17. return obj;
  18. }

2.3 向循环队列插入一个元素。如果成功插入则返回真。 

我们要往循环队列中插入一个元素,那么首先必须确保队列不为满(后面会封装),那我们之前分析过队列不为满的情况是rear指针的下一个位置是front,但是我们要注意一个特殊情况,如下图:

  1. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
  2. {
  3. if (myCircularQueueIsFull(obj))
  4. return false;
  5. obj->a[obj->rear] = value;
  6. obj->rear++;
  7. obj->rear %= (obj->capacity + 1);
  8. return true;
  9. }

 但是我们要注意的是实际上我们是多开了一个空间!!!%的时候要把多的空间算上

2.4 向循环队列删除一个元素。如果成功删除则返回真。

我们要往循环队列中删除一个元素,那么首先必须确保队列不为空(后面会封装),front++就行了,同样front也会遇到上面这种情况,处理当时一样,++完后%上数组的实际大小

  1. bool myCircularQueueDeQueue(MyCircularQueue* obj)
  2. {
  3. if (myCircularQueueIsEmpty(obj))
  4. return false;
  5. obj->front++;
  6. obj->front %= (obj->capacity + 1);
  7. return true;
  8. }

2.5 从队首获取元素。如果队列为空,返回 - 1 

直接取头指针就行了

  1. int myCircularQueueFront(MyCircularQueue* obj)
  2. {
  3. if (myCircularQueueIsEmpty(obj))
  4. return -1;
  5. return obj->a[obj->front];
  6. }

2.6 从队尾获取元素。如果队列为空,返回 - 1 

要注意rear指针指向的是最后一个元素的下一个位置,所以要取得的话就要找到rear的前一个位置的下标,这里我们不能直接让rear--,因为我们只是获取队列尾的元素,并不能去改变rear的指向,所以我们要算出rear前面那个位置的下标,其实也是一样,利用%的修正,让rear加上数组实际大小-1,然后再%数组的大小,就刚还是rear前面的位置的下标了!!

  1. int myCircularQueueRear(MyCircularQueue* obj)
  2. {
  3. if (myCircularQueueIsEmpty(obj))
  4. return -1;
  5. return obj->a[(obj->rear + obj->capacity) % (obj->capacity + 1)];
  6. }

2.7 判断循环队列是否为空

  1. bool myCircularQueueIsEmpty(MyCircularQueue* obj)
  2. {
  3. return obj->front == obj->rear;
  4. }

2.8 判断循环队列是否为满

  1. bool myCircularQueueIsFull(MyCircularQueue* obj)
  2. {
  3. return (obj->rear + 1)%(obj->capacity + 1) ==obj->front;//rear为k的时候正好
  4. }

2.9 销毁循环队列

  1. void myCircularQueueFree(MyCircularQueue* obj)
  2. {
  3. free(obj->a);//没必要置空,因为obj用不了,obj->a也用不了 front rear k 也没必要释放
  4. free(obj);
  5. //obj = NULL;
  6. }

2.10 全部代码

2.10.1 MyCircularQueue.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdbool.h>
  4. #include<stdlib.h>
  5. #include<assert.h>
  6. typedef int QDataType;
  7. typedef struct MyCircularQueue
  8. {
  9. QDataType* a;//动态数组
  10. int capacity;//记录循环队列的容量
  11. int front;//记录队首
  12. int rear;//记录队尾
  13. } MyCircularQueue;
  14. MyCircularQueue* myCircularQueueCreate(int k);//构造一个k长度的队列
  15. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value);// 向循环队列插入一个元素。如果成功插入则返回真。
  16. bool myCircularQueueDeQueue(MyCircularQueue* obj);// 向循环队列删除一个元素。如果成功删除则返回真。
  17. int myCircularQueueFront(MyCircularQueue* obj); //从队首获取元素。如果队列为空,返回 - 1 。
  18. int myCircularQueueRear(MyCircularQueue* obj);//从队尾获取元素。如果队列为空,返回 - 1 。
  19. bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空
  20. bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满
  21. void myCircularQueueFree(MyCircularQueue* obj);//销毁循环队列

 2.10.2 MyCircularQueue.c

  1. #include"MyCircularQueue.h"
  2. MyCircularQueue* myCircularQueueCreate(int k)
  3. {
  4. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  5. if (obj == NULL)
  6. {
  7. perror("malloc obj fail");
  8. exit(1);
  9. }
  10. obj->a = (QDataType*)malloc(sizeof(QDataType) * (k + 1));
  11. if (obj->a == NULL)
  12. {
  13. perror("malloc obj->a fail");
  14. exit(1);
  15. }
  16. obj->front = obj->rear = 0;
  17. obj->capacity = k;
  18. return obj;
  19. }
  20. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
  21. {
  22. if (myCircularQueueIsFull(obj))
  23. return false;
  24. obj->a[obj->rear] = value;
  25. obj->rear++;
  26. obj->rear %= (obj->capacity + 1);
  27. return true;
  28. }
  29. bool myCircularQueueDeQueue(MyCircularQueue* obj)
  30. {
  31. if (myCircularQueueIsEmpty(obj))
  32. return false;
  33. obj->front++;
  34. obj->front %= (obj->capacity + 1);
  35. return true;
  36. }
  37. int myCircularQueueFront(MyCircularQueue* obj)
  38. {
  39. if (myCircularQueueIsEmpty(obj))
  40. return -1;
  41. return obj->a[obj->front];
  42. }
  43. int myCircularQueueRear(MyCircularQueue* obj)
  44. {
  45. if (myCircularQueueIsEmpty(obj))
  46. return -1;
  47. return obj->a[(obj->rear + obj->capacity) % (obj->capacity + 1)];
  48. }
  49. bool myCircularQueueIsEmpty(MyCircularQueue* obj)
  50. {
  51. return obj->front == obj->rear;
  52. }
  53. bool myCircularQueueIsFull(MyCircularQueue* obj)
  54. {
  55. return (obj->rear + 1)%(obj->capacity + 1) ==obj->front;//rear为k的时候正好
  56. }
  57. void myCircularQueueFree(MyCircularQueue* obj)
  58. {
  59. free(obj->a);//没必要置空,因为obj用不了,obj->a也用不了 front rear k 也没必要释放
  60. free(obj);
  61. //obj = NULL;
  62. }

2.11 相关选择题

5.现有一循环队列,其队头指针为front,队尾指针为rear;循环队列长度为N。其队内有效长度为?(假设队头不存放数据)( ?)
A (rear - front + N) % N + 1
B (rear - front + N) % N
C (rear - front) % (N + 1)
D (rear - front + N) % (N - 1)

答:这题就是根据我们上面那道题的一个变形,所以我们知道肯定是%上长度的,所以可以直接选B

三、链式结构实现循环队列

怎么用链式结构来实现循环队列呢?我们来分析一下:

3.1 结构体的创建

我们按照链式队列的思路,创建一个队列结构体来管理头尾指针,同时加上capacity(容量)和size(有效数据)

  1. typedef int QDataType;
  2. typedef struct QNode
  3. {
  4. struct QNode* next;//节点
  5. QDataType val;//数据域
  6. }QNode;
  7. typedef struct MyCircularQueue
  8. {
  9. QNode* front;//链表的头指针
  10. QNode* rear;//链表的尾指针
  11. int capacity;//记录链表的容量
  12. int size;//记录链表的有效节点
  13. }MyCircularQueue;

3.2 构造一个k长度的队列并返回

  1. MyCircularQueue* myCircularQueueCreate(int k)
  2. {
  3. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  4. if (obj == NULL)
  5. {
  6. perror("malloc fail");
  7. exit(1);
  8. }
  9. obj->front = obj->rear = NULL;
  10. obj->size = 0;
  11. obj->capacity = k;
  12. return obj;
  13. }

3.3 向循环队列插入一个元素。如果成功插入则返回真。

  1. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
  2. {
  3. //如果为满了,直接就返回false
  4. if (myCircularQueueIsFull(obj))
  5. return false;
  6. //不满足就创建节点
  7. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  8. if (newnode == NULL)
  9. {
  10. perror("malloc fail");
  11. exit(1);
  12. }
  13. newnode->val = value;
  14. newnode->next = NULL;
  15. //创建成功,要考虑队列为空和不为空的情况
  16. if (myCircularQueueIsEmpty(obj))//为空,让新节点成为头
  17. obj->front = obj->rear = newnode;
  18. else//不为空,让tail继续往后遍历
  19. {
  20. obj->rear->next = newnode;
  21. obj->rear = newnode;
  22. }
  23. obj->size++;
  24. return true;
  25. }

3.4 向循环队列删除一个元素。如果成功删除则返回真。

  1. bool myCircularQueueDeQueue(MyCircularQueue* obj)
  2. {
  3. //为空就没有删的必要了
  4. if (myCircularQueueIsEmpty(obj))
  5. return false;
  6. //不为空,删除头节点,让下一个节点成为新的头,然后释放掉
  7. QNode* cur = obj->front->next;
  8. free(obj->front);
  9. obj->front = cur;
  10. obj->size--;
  11. return true;
  12. }

3.5 从队首获取元素。如果队列为空,返回 - 1 

  1. int myCircularQueueFront(MyCircularQueue* obj)
  2. {
  3. //为空,返回1
  4. if (myCircularQueueIsEmpty(obj))
  5. return -1;
  6. //不为空,就获取头指针的数据
  7. return obj->front->val;
  8. }

3.6 从队尾获取元素。如果队列为空,返回 - 1

  1. int myCircularQueueRear(MyCircularQueue* obj)
  2. {
  3. //为空,返回1
  4. if (myCircularQueueIsEmpty(obj))
  5. return -1;
  6. //不为空,就获取尾指针的数据
  7. return obj->rear->val;
  8. }

3.7 判断循环队列是否为空

  1. bool myCircularQueueIsEmpty(MyCircularQueue* obj)
  2. {
  3. return obj->size == 0;
  4. }

3.8 判断循环队列是否为满

  1. bool myCircularQueueIsFull(MyCircularQueue* obj)
  2. {
  3. return obj->size == obj->capacity;
  4. }

3.9 销毁循环队列

  1. void myCircularQueueFree(MyCircularQueue* obj)
  2. {
  3. //本质是链表,要一个个释放
  4. QNode* pcur = obj->front;//用来遍历删除的
  5. while (pcur)
  6. {
  7. QNode* next= pcur->next;
  8. free(pcur);
  9. pcur = next;
  10. }
  11. free(obj);
  12. }

3.10 全部代码

3.10.1 MyCircularQueue.h

  1. #pragma once
  2. #include<stdio.h>
  3. #include<stdbool.h>
  4. #include<stdlib.h>
  5. #include<assert.h>
  6. typedef int QDataType;
  7. typedef struct QNode
  8. {
  9. struct QNode* next;//节点
  10. QDataType val;//数据域
  11. }QNode;
  12. typedef struct MyCircularQueue
  13. {
  14. QNode* front;//链表的头指针
  15. QNode* rear;//链表的尾指针
  16. int capacity;//记录链表的容量
  17. int size;//记录链表的有效节点
  18. }MyCircularQueue;
  19. MyCircularQueue* myCircularQueueCreate(int k);//构造一个k长度的队列
  20. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value);// 向循环队列插入一个元素。如果成功插入则返回真。
  21. bool myCircularQueueDeQueue(MyCircularQueue* obj);// 向循环队列删除一个元素。如果成功删除则返回真。
  22. int myCircularQueueFront(MyCircularQueue* obj); //从队首获取元素。如果队列为空,返回 - 1 。
  23. int myCircularQueueRear(MyCircularQueue* obj);//从队尾获取元素。如果队列为空,返回 - 1 。
  24. bool myCircularQueueIsEmpty(MyCircularQueue* obj);//判断循环队列是否为空
  25. bool myCircularQueueIsFull(MyCircularQueue* obj);//判断循环队列是否为满
  26. void myCircularQueueFree(MyCircularQueue* obj);//销毁循环队列

3.10.2 MyCircularQueue.c

  1. MyCircularQueue* myCircularQueueCreate(int k)
  2. {
  3. MyCircularQueue* obj = (MyCircularQueue*)malloc(sizeof(MyCircularQueue));
  4. if (obj == NULL)
  5. {
  6. perror("malloc fail");
  7. exit(1);
  8. }
  9. obj->front = obj->rear = NULL;
  10. obj->size = 0;
  11. obj->capacity = k;
  12. }
  13. bool myCircularQueueEnQueue(MyCircularQueue* obj, QDataType value)
  14. {
  15. //如果为满了,直接就返回false
  16. if (myCircularQueueIsFull(obj))
  17. return false;
  18. //不满足就创建节点
  19. QNode* newnode = (QNode*)malloc(sizeof(QNode));
  20. if (newnode == NULL)
  21. {
  22. perror("malloc fail");
  23. exit(1);
  24. }
  25. newnode->val = value;
  26. newnode->next = NULL;
  27. //创建成功,要考虑队列为空和不为空的情况
  28. if (myCircularQueueIsEmpty(obj))//为空,让新节点成为头
  29. obj->front = obj->rear = newnode;
  30. else//不为空,让tail继续往后遍历
  31. {
  32. obj->rear->next = newnode;
  33. obj->rear = newnode;
  34. }
  35. obj->size++;
  36. return true;
  37. }
  38. bool myCircularQueueDeQueue(MyCircularQueue* obj)
  39. {
  40. //为空就没有删的必要了
  41. if (myCircularQueueIsEmpty(obj))
  42. return false;
  43. //不为空,删除头节点,让下一个节点成为新的头,然后释放掉
  44. QNode* cur = obj->front->next;
  45. free(obj->front);
  46. obj->front = cur;
  47. obj->size--;
  48. return true;
  49. }
  50. int myCircularQueueFront(MyCircularQueue* obj)
  51. {
  52. //为空,返回1
  53. if (myCircularQueueIsEmpty(obj))
  54. return -1;
  55. //不为空,就获取头指针的数据
  56. return obj->front->val;
  57. }
  58. int myCircularQueueRear(MyCircularQueue* obj)
  59. {
  60. //为空,返回1
  61. if (myCircularQueueIsEmpty(obj))
  62. return -1;
  63. //不为空,就获取尾指针的数据
  64. return obj->rear->val;
  65. }
  66. bool myCircularQueueIsEmpty(MyCircularQueue* obj)
  67. {
  68. return obj->size == 0;
  69. }
  70. bool myCircularQueueIsFull(MyCircularQueue* obj)
  71. {
  72. return obj->size == obj->capacity;
  73. }
  74. void myCircularQueueFree(MyCircularQueue* obj)
  75. {
  76. //本质是链表,要一个个释放
  77. QNode* pcur = obj->front;//用来遍历删除的
  78. while (pcur)
  79. {
  80. QNode* next= pcur->next;
  81. free(pcur);
  82. pcur = next;
  83. }
  84. free(obj);
  85. }

四、总结

        我们会发现,在这边无论是用数组实现和链表实现,本质上我们只是从逻辑层次上把它认为是相连的,但是物理层次上并没有把它连在一起,虽然链表是可以做到相连的,但是相连的话会比较复杂,不相连我们也可以解决,只要保证我们能够控制得住边界问题就行!! 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/很楠不爱3/article/detail/125885
推荐阅读
相关标签
  

闽ICP备14008679号