深圳幻海软件技术有限公司 欢迎您!

数据结构:循环队列的实现(leetcode622.设计循环队列)

2023-03-14

 目录一.循环队列简单介绍二.用静态数组实现循环队列1.数组循环队列结构设计2.数组循环队列的堆区内存申请接口 3.数据出队和入队的接口实现4.其他操作接口5.数组循环队列的实现代码总览 三.静态单向循环链表实现循环队列 1.链表循环队列的结构设计2.创建静态

 

目录

一.循环队列简单介绍

二.用静态数组实现循环队列

1.数组循环队列结构设计

2.数组循环队列的堆区内存申请接口 

3.数据出队和入队的接口实现

4.其他操作接口

5.数组循环队列的实现代码总览 

三.静态单向循环链表实现循环队列 

1.链表循环队列的结构设计

2.创建静态单向循环链表的接口

3.数据的出队和入队接口

4.其他队列操作接口

5.静态链表循环队列总体代码


问题来源:622. 设计循环队列 - 力扣(Leetcode)

一.循环队列简单介绍

  • 循环队列一般是一种静态的线性数据结构,其中的数据符合先进先出的原则.
  • 循环队列的容器首地址容器尾地址通过特定操作(比如指针链接,数组下标取余等方式)相连通,从而实现了容器空间的重复利用(在一个非循环静态队列里,一旦一个队列满了,我们就不能插入下一个元素,即使在队列前面仍有空间)

 

二.用静态数组实现循环队列

维护队列的结构体:

  1. typedef struct
  2. {
  3. int * arry; //指向堆区数组的指针
  4. int head; //队头指针
  5. int tail; //队尾指针(指向队尾数据的下一个位置)(不指向有效数据)
  6. int capacity;//静态队列的容量
  7. } MyCircularQueue;

1.数组循环队列结构设计

我们假定静态数组的容量为k(可存储k个数据)

  • 根据队列的基本数据结构:有两个指针用于维护数组中的有效数据空间,分别为head指针和tail指针,head指针用于指向队头数据,tail用于指向队尾数据的下一个位置(即tail指针不指向有效数据)
  •  如图所示,head指针和tail指针之间就是有效数据的内存空间
  • 通过head指针和tail指针的关系来实现队列的判满(判断队列空间是否已被占满)与判空(判断队列是否为空队列);为了达到这个目的,我们需要将静态数组的容量大小设置为k+1(即多设置一个元素空间) 
  1. 队列的判空条件: tail == head;
  2. 队列的判满条件: (tail+1)%(k+1) == head; 另外一种情形:
  • 由此我们可以先设计出队列的判满和判空接口
    1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空
    2. {
    3. assert(obj);
    4. return (obj->tail == obj->head);
    5. }
    6. bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满
    7. {
    8. assert(obj);
    9. return ((obj->tail+1)%(obj->capacity +1) == obj->head);
    10. }

2.数组循环队列的堆区内存申请接口 

  • 堆区上创建MyCircularQueue结构体,同时为队列申请一个空间大小为(k+1)*sizeof(DataType)字节的数组:
    1. MyCircularQueue* myCircularQueueCreate(int k) //k个容量大小的循环队列的初始化接口
    2. {
    3. MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
    4. //开辟维护循环队列的结构体
    5. if(NULL == tem)
    6. {
    7. perror("malloc failed");
    8. exit(-1);
    9. }
    10. tem->arry = NULL;
    11. tem->capacity = k;
    12. //队列的数据容量为k
    13. tem->arry = (int*)malloc((k+1)*sizeof(int));
    14. //开辟堆区数组
    15. if(NULL == tem->arry)
    16. {
    17. perror("malloc failed");
    18. exit(-1);
    19. }
    20. //将head,tail下标初始化为0
    21. tem->head = 0;
    22. tem->tail = 0;
    23. return tem;
    24. }

3.数据出队和入队的接口实现

数据出队和入队的图解:

 

  •  根据图解我们可以设计出数据入队和出队的接口:
    1. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口
    2. {
    3. assert(obj);
    4. if(myCircularQueueIsFull(obj))
    5. {
    6. return false;
    7. }
    8. //确保队列没满
    9. obj->arry[obj->tail]=value;
    10. obj->tail = (obj->tail + 1)%(obj->capacity +1);
    11. return true;
    12. }
    1. bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口
    2. {
    3. assert(obj);
    4. if(myCircularQueueIsEmpty(obj))
    5. {
    6. return false;
    7. }
    8. //确保队列不为空
    9. obj->head = (obj->head +1)%(obj->capacity +1);
    10. return true;
    11. }

4.其他操作接口

返回队头数据的接口:

  1. int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口
  2. {
  3. assert(obj);
  4. if(myCircularQueueIsEmpty(obj))
  5. {
  6. return -1;
  7. }
  8. return obj->arry[obj->head];
  9. }

 返回队尾数据的接口:

  1. int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口
  2. {
  3. assert(obj);
  4. if(myCircularQueueIsEmpty(obj))
  5. {
  6. return -1;
  7. }
  8. int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity;
  9. //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置
  10. return obj->arry[labelret];
  11. }

队列的销毁接口:

  1. void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
  2. {
  3. assert(obj);
  4. free(obj->arry);
  5. obj->arry = NULL;
  6. free(obj);
  7. obj = NULL;
  8. }

5.数组循环队列的实现代码总览 

 数组循环队列总体代码:

  1. typedef struct
  2. {
  3. int * arry; //指向堆区数组的指针
  4. int head; //队头指针
  5. int tail; //队尾指针(指向队尾数据的下一个位置)(不指向有效数据)
  6. int capacity;//静态队列容量
  7. } MyCircularQueue;
  8. bool myCircularQueueIsEmpty(MyCircularQueue* obj);
  9. bool myCircularQueueIsFull(MyCircularQueue* obj);
  10. //顺序编译注意:先被使用而后被定义的函数要记得进行声明
  11. MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口
  12. {
  13. MyCircularQueue * tem = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
  14. //开辟维护循环队列的结构体
  15. if(NULL == tem)
  16. {
  17. perror("malloc failed");
  18. exit(-1);
  19. }
  20. tem->arry = NULL;
  21. tem->capacity = k;
  22. //队列的数据容量为k
  23. tem->arry = (int*)malloc((k+1)*sizeof(int));
  24. //开辟堆区数组
  25. if(NULL == tem->arry)
  26. {
  27. perror("malloc failed");
  28. exit(-1);
  29. }
  30. tem->head = 0;
  31. tem->tail = 0;
  32. return tem;
  33. }
  34. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口
  35. {
  36. assert(obj);
  37. if(myCircularQueueIsFull(obj))
  38. {
  39. return false;
  40. }
  41. //确保队列没满
  42. obj->arry[obj->tail]=value;
  43. obj->tail = (obj->tail + 1)%(obj->capacity +1);
  44. return true;
  45. }
  46. bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口
  47. {
  48. assert(obj);
  49. if(myCircularQueueIsEmpty(obj))
  50. {
  51. return false;
  52. }
  53. //确保队列不为空
  54. obj->head = (obj->head +1)%(obj->capacity +1);
  55. return true;
  56. }
  57. int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口
  58. {
  59. assert(obj);
  60. if(myCircularQueueIsEmpty(obj))
  61. {
  62. return -1;
  63. }
  64. return obj->arry[obj->head];
  65. }
  66. int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口
  67. {
  68. assert(obj);
  69. if(myCircularQueueIsEmpty(obj))
  70. {
  71. return -1;
  72. }
  73. int labelret = ((obj->tail-1)>=0)? obj->tail-1 : obj->capacity;
  74. //注意tail如果指向数组首地址,则尾数据位于数组最后一个位置
  75. return obj->arry[labelret];
  76. }
  77. bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空
  78. {
  79. assert(obj);
  80. return (obj->tail == obj->head);
  81. }
  82. bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满
  83. {
  84. assert(obj);
  85. return ((obj->tail+1)%(obj->capacity +1) == obj->head);
  86. }
  87. void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
  88. {
  89. assert(obj);
  90. free(obj->arry);
  91. obj->arry = NULL;
  92. free(obj);
  93. obj = NULL;
  94. }

力扣题解测试:

三.静态单向循环链表实现循环队列 

链表节点结构体定义:

  1. typedef struct listnode
  2. {
  3. int data;
  4. struct listnode * next;
  5. }ListNode;

维护链表循环队列的结构体:

  1. typedef struct
  2. {
  3. int capacity; //记录队列容量大小
  4. ListNode * head; //指向队头节点
  5. ListNode * tail; //指向队尾节点
  6. } MyCircularQueue;

1.链表循环队列的结构设计

静态单向循环链表的容量大小为k:

  • 与数组循环队列类似,我们同样需要开辟一个节点个数为k+1的静态循环链表
  • 链表循环队列的总体结构图示:另外一种队列判满的情形:
  1.  链表循环队列的判满条件(判断队列空间是否被占满的关系式):tail->next == head;
  2.  链表循环队列的判空条件(判断队列是否为空队列的关系式): tail == head;

链表循环队列的判满和判空的接口:

  1. bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空
  2. {
  3. assert(obj);
  4. return(obj->head == obj->tail);
  5. }
  6. bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满
  7. {
  8. assert(obj);
  9. return (obj->tail->next == obj->head);
  10. }

2.创建静态单向循环链表的接口

实现一个接口,创建一个维护链表循环队列的结构体同时创建容量大小为k+1的静态单向循环链表:

  1. MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口
  2. {
  3. int NodeNum =k+1; //创建k+1个链表节点
  4. MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
  5. assert(object); //申请维护循环队列的结构体
  6. object->capacity = k;
  7. ListNode * preNode = NULL; //用于记录前一个链接节点的地址
  8. while(NodeNum)
  9. {
  10. if(NodeNum == k+1)
  11. { ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
  12. assert(tem);
  13. preNode = tem;
  14. object->tail = object->head=tem; //让tail和head指向同一个初始节点
  15. }
  16. else
  17. {
  18. ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
  19. assert(tem);
  20. preNode->next = tem; //链接链表节点
  21. preNode = tem;
  22. }
  23. NodeNum--;
  24. }
  25. preNode->next = object->head; //将表尾与表头相接
  26. return object;
  27. }

3.数据的出队和入队接口

数据出入队图解:

根据图解实现数据出入队接口:

  1. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value)//数据入队接口(从队尾入队)
  2. {
  3. assert(obj);
  4. if(!obj || myCircularQueueIsFull(obj)) //确定队列没满
  5. {
  6. return false;
  7. }
  8. obj->tail->data = value; //数据入队
  9. obj->tail = obj->tail->next;
  10. return true;
  11. }
  1. bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口
  2. {
  3. assert(obj);
  4. if(!obj || myCircularQueueIsEmpty(obj))
  5. {
  6. return false;
  7. }
  8. //数据出队
  9. obj->head = obj->head->next;
  10. return true;
  11. }

4.其他队列操作接口

返回队头数据的接口:

  1. int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口
  2. {
  3. assert(obj);
  4. if(myCircularQueueIsEmpty(obj))
  5. {
  6. return -1;
  7. }
  8. return obj->head->data; //返回队头元素
  9. }

返回队尾数据的接口:

  1. int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口
  2. {
  3. assert(obj);
  4. if(myCircularQueueIsEmpty(obj))
  5. {
  6. return -1;
  7. }
  8. ListNode * tem = obj->head;
  9. while(tem->next != obj->tail) //寻找队尾元素
  10. {
  11. tem=tem->next;
  12. }
  13. return tem->data; //返回队尾元素
  14. }

队列销毁接口:

队列销毁过程图解:

  1. void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
  2. {
  3. assert(obj);
  4. //利用头指针来完成链表节点的释放
  5. ListNode * endpoint = obj->head; //记录一个节点释放的终点
  6. obj->head = obj->head->next;
  7. while(obj->head!=endpoint)
  8. {
  9. ListNode * tem = obj->head->next;
  10. free(obj->head);
  11. obj->head = tem;
  12. }
  13. free(endpoint); //释放掉终点节点
  14. free(obj); //释放掉维护环形队列的结构体
  15. }

5.静态链表循环队列总体代码

总体代码:

  1. typedef struct listnode
  2. {
  3. int data;
  4. struct listnode * next;
  5. }ListNode;
  6. typedef struct
  7. {
  8. int capacity;
  9. ListNode * head;
  10. ListNode * tail;
  11. int taildata; //单向链表找尾复杂度为O(N),因此我们用一个变量来记录队尾数据
  12. } MyCircularQueue;
  13. bool myCircularQueueIsEmpty(MyCircularQueue* obj);
  14. bool myCircularQueueIsFull(MyCircularQueue* obj);
  15. //顺序编译注意:先被使用而后被定义的函数要记得进行声明
  16. MyCircularQueue* myCircularQueueCreate(int k) //循环队列初始化接口
  17. {
  18. int NodeNum =k+1; //创建k+1个链表节点
  19. MyCircularQueue* object = (MyCircularQueue *)malloc(sizeof(MyCircularQueue));
  20. assert(object); //申请维护循环队列的结构体
  21. object->capacity = k;
  22. ListNode * preNode = NULL; //用于记录前一个链接节点的地址
  23. while(NodeNum)
  24. {
  25. if(NodeNum == k+1)
  26. { ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
  27. assert(tem);
  28. preNode = tem;
  29. object->tail = object->head=tem; //让tail和head指向同一个初始节点
  30. }
  31. else
  32. {
  33. ListNode * tem = (ListNode *)malloc(sizeof(ListNode));
  34. assert(tem);
  35. preNode->next = tem; //链接链表节点
  36. preNode = tem;
  37. }
  38. NodeNum--;
  39. }
  40. preNode->next = object->head; //将表尾与表头相接
  41. return object;
  42. }
  43. bool myCircularQueueEnQueue(MyCircularQueue* obj, int value) //数据入队接口(从队尾入队)
  44. {
  45. assert(obj);
  46. if(!obj || myCircularQueueIsFull(obj)) //确定队列没满
  47. {
  48. return false;
  49. }
  50. obj->tail->data = value; //数据入队
  51. obj->tail = obj->tail->next;
  52. return true;
  53. }
  54. bool myCircularQueueDeQueue(MyCircularQueue* obj) //数据出队接口
  55. {
  56. assert(obj);
  57. if(!obj || myCircularQueueIsEmpty(obj))
  58. {
  59. return false;
  60. }
  61. obj->head = obj->head->next;
  62. return true;
  63. }
  64. int myCircularQueueFront(MyCircularQueue* obj) //返回队头数据的接口
  65. {
  66. assert(obj);
  67. if(myCircularQueueIsEmpty(obj))
  68. {
  69. return -1;
  70. }
  71. return obj->head->data;
  72. }
  73. int myCircularQueueRear(MyCircularQueue* obj) //返回队尾数据的接口
  74. {
  75. assert(obj);
  76. if(myCircularQueueIsEmpty(obj))
  77. {
  78. return -1;
  79. }
  80. ListNode * tem = obj->head;
  81. while(tem->next != obj->tail) //寻找队尾元素
  82. {
  83. tem=tem->next;
  84. }
  85. return tem->data;
  86. }
  87. bool myCircularQueueIsEmpty(MyCircularQueue* obj) //判断队列是否为空
  88. {
  89. assert(obj);
  90. return(obj->head == obj->tail);
  91. }
  92. bool myCircularQueueIsFull(MyCircularQueue* obj) //判断队列是否为满
  93. {
  94. assert(obj);
  95. return (obj->tail->next == obj->head);
  96. }
  97. void myCircularQueueFree(MyCircularQueue* obj) //销毁队列的接口
  98. {
  99. assert(obj);
  100. //利用头指针来完成链表节点的释放
  101. ListNode * endpoint = obj->head; //记录一个节点释放的终点
  102. obj->head = obj->head->next;
  103. while(obj->head!=endpoint)
  104. {
  105. ListNode * tem = obj->head->next;
  106. free(obj->head);
  107. obj->head = tem;
  108. }
  109. free(endpoint); //释放掉终点节点
  110. free(obj); //释放掉维护环形队列的结构体
  111. }

leetcode题解测试:

 

 

 

文章知识点与官方知识档案匹配,可进一步学习相关知识
算法技能树首页概览40346 人正在系统学习中