• 循环队列----数据结构


    缘由

    在队列的顺序存储中,采用第二种出队的方式,将头指针 +1 ,可以避免元素的移动,但是这样也出现了一个问题 "假溢出" ,如图:

    当出现这种情况时:头指针和尾指针都指向了不可访问的地方(越界了),就无法在插入(入队)了,队列的空间还空着,却无法利用,这造成了空间的浪费。

    为了使用到完全的空间(利用前面的空间),可以使用循环队列。(红色代表已经利用的空间)

    如图,当 tail 的值为5时,他应该指向下标为 0 的空间,也就是将 tail 赋值为0。这样队列就首尾相连,变成了循环队列。

    由于 头指针和尾指针的范围是 [ 0 , MAX_SIZE -1 ] ,一旦等于MAX_SIZE,就变为0,所以可以使用到取模操作,每次移动完,再对 MAX_SIZE 取模。

    队列的判空判满

    那出现下面这种情况还可以插入元素吗?如下图:

    假如可以的话,再插入一个元素,那 tail +1 就为 3 ,这时会发现一个问题:该如何判断队列为空,如何判断队列满了,有些困难。

    所以这种情况就无法再插入元素了,我们能使用到的空间只有 MAX_SIZE-1 个,其中一块空间是为了方便判断队列的状态,并不保存任何数据。

     判断队列为空的条件是:头指针与尾指针指向同一个位置;判断队列为满的条件是:尾指针的后一位是头指针。

     tail == head 为判空条件,结合之前的移动问题,所以不是 tail + 1 == head,而是 (tail + 1) % MAX_SIZE == head 为判满条件

    如图( front 是头指针,rear 是尾指针):

    获取元素个数

    顺序队列中求元素个数的方法为 尾指针减去头指针 ,可是现在有两种情况:

    一、头指针在尾指针的前面(tail >= head):元素个数为 tail - head

    二、尾指针在头指针的前面(tail

    取模操作可以将两种情况统一为一种:元素个数为 (head - tail + MAX_SIZE) % MAX_SIZE

    具体实现

    就是顺序队列改动了一下需要注意的点。

    1. #include <iostream>
    2. using namespace std;
    3. //循环队列的定义
    4. #define MAX_SIZE 5
    5. typedef int DateElem;
    6. typedef struct Queue
    7. {
    8. DateElem date[MAX_SIZE];
    9. int head; //头指针
    10. int tail; //尾指针
    11. }squeue;
    12. //初始化循环队列
    13. void InitQueue(squeue* sq)
    14. {
    15. if (!sq) return;
    16. sq->head = 0;
    17. sq->tail = 0;
    18. }
    19. //判断循环队列是否满了
    20. bool IsFull(squeue* sq)
    21. {
    22. if (!sq) return false;
    23. if ((sq->tail+1)%MAX_SIZE == sq->head) //*
    24. {
    25. return true;
    26. }
    27. else
    28. {
    29. return false;
    30. }
    31. }
    32. //判断循环队列是否为空
    33. bool IsEmpty(squeue* sq)
    34. {
    35. if (!sq) return false;
    36. if (sq->head == sq->tail)
    37. {
    38. return true;
    39. }
    40. else
    41. {
    42. return false;
    43. }
    44. }
    45. //循环队列入队
    46. bool EnterQueue(squeue* sq, DateElem e)
    47. {
    48. if (IsFull(sq))
    49. {
    50. cout << "无法插入元素" << e << ",队列已满。" << endl;
    51. return false;
    52. }
    53. sq->date[sq->tail] = e;
    54. sq->tail = (sq->tail + 1) % MAX_SIZE; //*
    55. return true;
    56. }
    57. //循环队列出队
    58. bool PopQueue(squeue* sq, DateElem* date)
    59. {
    60. if (!sq || IsEmpty(sq))
    61. {
    62. return false;
    63. }
    64. *date = sq->date[sq->head];
    65. sq->head = (sq->head + 1) % MAX_SIZE; //*
    66. return true;
    67. }
    68. //打印队列
    69. bool PrintQueue(squeue* sq)
    70. {
    71. if (!sq) return false;
    72. for (int i = sq->head; i != sq->tail; i = (i+1) %MAX_SIZE) //*
    73. {
    74. printf("%d ", sq->date[i]);
    75. }
    76. return true;
    77. }
    78. //获取队首元素
    79. int GetHeadElem(squeue* sq)
    80. {
    81. if (!sq || IsEmpty(sq)) return 0;
    82. return sq->date[sq->head];
    83. }
    84. //销毁(清空)队列
    85. bool DestoryQueue(squeue* sq)
    86. {
    87. if (!sq) return false;
    88. sq->head = 0;
    89. sq->tail = 0;
    90. return true;
    91. }
    92. //获取队列长度
    93. int GetLength(squeue* sq)
    94. {
    95. if (!sq) return 0;
    96. return (sq->tail - sq->head + MAX_SIZE) % MAX_SIZE; //*
    97. }
    98. int main(void)
    99. {
    100. squeue* sq = new squeue;
    101. DateElem* s = new DateElem;
    102. InitQueue(sq);
    103. DateElem e = 0;
    104. int choose = -1;
    105. while (choose != 0)
    106. {
    107. cout << "1.入队" << endl
    108. << "2.出队" << endl
    109. << "3.打印队列" << endl
    110. << "4.获取队首元素" << endl
    111. << "5.获取队列长度" << endl
    112. << "6.销毁队列" << endl
    113. << "0.退出" << endl;
    114. cin >> choose;
    115. switch (choose)
    116. {
    117. case 1:
    118. cout << "请输入要入队的元素:";
    119. cin >> e;
    120. if (EnterQueue(sq, e))
    121. {
    122. cout << "入队成功" << endl;
    123. }
    124. else
    125. {
    126. cout << "入队失败" << endl;
    127. }
    128. break;
    129. case 2:
    130. if (PopQueue(sq, s))
    131. {
    132. cout << "出队的元素是:" << *s << endl;
    133. }
    134. else
    135. {
    136. cout << "出队失败" << endl;
    137. }
    138. break;
    139. case 3:
    140. cout << "队列中的元素是:";
    141. PrintQueue(sq);
    142. cout << endl;
    143. break;
    144. case 4:
    145. cout << "队首元素是:" << GetHeadElem(sq) << endl;
    146. break;
    147. case 5:
    148. cout << "队列的长度是:" << GetLength(sq) << endl;
    149. break;
    150. case 6:
    151. if (DestoryQueue(sq))
    152. {
    153. cout << "队列已销毁" << endl;
    154. }
    155. else
    156. {
    157. cout << "队列不存在" << endl;
    158. }
    159. break;
    160. case 0:
    161. cout << "退出成功" << endl;
    162. break;
    163. default:
    164. cout << "输入非法" << endl;
    165. break;
    166. }
    167. }
    168. return 0;
    169. }

  • 相关阅读:
    Lua数值 - number
    使用React和ResizeObserver实现自适应ECharts图表
    【观察】联想“以行践言”,赋能专精特新驶入成长“快车道”
    关于地图GIS开发事项的一次实践整理(上)
    前端基础建设与架构04 横向对比主流构建工具,了解构建工具的设计考量
    区块链 - 各个国家Web3的现状与趋势
    D2-读论文D2&算法题D2(复习:单链表、双链表、模拟栈)
    【前端】HTTP —— HTTP 协议中的细节(超详细!!)
    MySQL read 查询语句8 主键 外键
    MySQL主从复制原理和使用
  • 原文地址:https://blog.csdn.net/qq_66805048/article/details/133960346