一、队列简介

定义

队列(queue)在计算机科学中,是一种先进先出的线性表。 它只允许在表的前端进行删除操作,而在表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。

  1. 1. 队列是一种线性结构;
  2. 2. 相比数组,队列对应操作的是数组的子集;
  3. 3. 只能从一端(队尾)添加元素,只能从另一端(队首)取出元素 。先进先出的数据结构(先到先得First In First OutFIFO】)。

二、代码实现

1. 队列接口

  1. public interface Queue{
  2. int getSize(); //返回元素的个数
  3. E getFront(); //返回队首元素内容
  4. boolean isEmpty(); //判断是否为空
  5. void enqueue(E e); // 入队
  6. E dequeue(); //出队
  7. }

2、循环队列

循环队列中有两个新词,两个指针

  • front 指向队列的第一个元素,初始指向0
  • tail 指向队列的最后一个元素的后一个位置,初始指向0
  • 循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。 [1]  循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
  1. public class LoopQueue<E> implements Queue<E> {
  2.  
  3. private E[] data;
  4. //指向队列的第一个元素,初始指向0
  5. private int front;
  6. //指向队列的最后一个元素的后一个位置,初始指向0
  7. private int tail;
  8. //元素数量
  9. private int size;
  10.  
  11. public LoopQueue(int capacity){
  12. data = (E[]) new Object[capacity + 1];
  13. front = 0;
  14. tail = 0;
  15. size = 0;
  16. }
  17. public LoopQueue(){
  18. this(10);
  19. }
  20.  
  21. @Override
  22. public int getSize() {
  23. return size;
  24. }
  25. /**
  26. * 因为容量放的时候多了个1,所以get容量的时候,需要减1
  27. * @return
  28. */
  29. public int getCapacity(){
  30. return data.length - 1;
  31. }
  32. /**
  33. * 当front和tail的值相等时,队列为空,初始两个指向的是同一个值(只有初始的时候,指向的是同一个地方)
  34. * @return
  35. */
  36. @Override
  37. public boolean isEmpty() {
  38. return front == tail;
  39. }
  40. /**
  41. * 1.if((tail + 1) % data.length == front) 如果tail + 1 超过了data.length的大小,
  42. * 代表当前tail指向已经超出了容量的大小,因为是循环式,所以需要tail去循环头元素中查看值是否有被占用,
  43. * 如果 == front 代表循环头没有,就需要扩容了。
  44. * 2.举例: 元素容量为8,tail目前指向7 front 指向2
  45. * if((7 + 1) % 8 == 2 ) if(0 == 2) 这里是false,因为front指向了2,所以代表 第0,1位是没有值的
  46. * 所以这个值需要在在第0位放(空间利用)
  47. * 3.data[tail] = param tail当前指向的地方需要赋值,然后tail自增 循环体 的1,size+1
  48. * @param param
  49. */
  50. @Override
  51. public void enqueue( E param) {
  52. if ((tail + 1) % data.length == front) {
  53. resize(getCapacity() * 2);
  54. }
  55. data[tail] = param;
  56. tail = (tail + 1) % data.length;
  57. size++;
  58. }
  59. /**
  60. * 1.如果队列为空抛出异常
  61. * 2.用ret变量来接受当前队列头的值
  62. * 3.接收成功之后将,队列头元素置空
  63. * 4.front指针指向下一个元素
  64. * 5.size大小-1
  65. * 6.如果size大小占据了容量的1/4和size为容量的1/2且不等于0的时候,对容量进行缩减,缩减为原来容量的1/2
  66. * 7.返回ret变量
  67. * @return
  68. */
  69. @Override
  70. public E dequeue() {
  71. if(isEmpty()){
  72. throw new IllegalArgumentException("Cannot dequeue from an empty queue");
  73. }
  74. E ret = data[front];
  75. data[front] = null;
  76. front = (front + 1) % data.length;
  77. size --;
  78. if (size == getCapacity() / 4 && getCapacity() / 2 != 0){
  79. resize(getCapacity()/2);
  80. }
  81. return ret;
  82. }
  83.  
  84. @Override
  85. public E getFront() {
  86. if (isEmpty())
  87. throw new IllegalArgumentException("Queue is empty");
  88. return data[front];
  89. }
  90. /**
  91. * 扩充队列的容量
  92. * 1.front代表了当前元素初始位置的指向
  93. * 2.newData的第i位元素,应该等于 i + front % data.length 的值
  94. * 3.举例:元素容量20,i 等于 0 ,front 等于 2,结果: newData[0] = data[(0 + 2) % 20]
  95. * = data[2] 意思就是,newData的第一位元素,应该等于data有值的第一位元素
  96. * % data.length 的原因主要是为了防止数组越界错误
  97. * 4.新数组赋值完成需要将 front 重新指向0,因为新数组的front指针是从0开始的。
  98. * tail最后要指向等于size大小的值,
  99. * @param newCapacity
  100. */
  101. private void resize(int newCapacity){
  102. E[] newData = (E[]) new Object[newCapacity + 1];
  103. for (int i=0; i < size; i++){
  104. newData[i] = data[(i + front) % data.length];
  105. }
  106. data = newData;
  107. front = 0;
  108. tail = size;
  109. }
  110. /**
  111. * 1.元素从 front位置开始循环遍历,i的值不能等于tail,
  112. * 也就是到tail的前一位,i = i + 1 且%data.length,
  113. * 因为i有可能从循环头重新开始
  114. * 2.( i + 1 ) % data.length != tail 如果当前i + 1 % data.length
  115. * 不等于tail表示不到最后一个元素,就拼接,
  116. * @return
  117. */
  118. @Override
  119. public String toString(){
  120. StringBuilder stringBuilder = new StringBuilder();
  121. stringBuilder.append(String.format("LoopQueue:size = %d, capacity = %d\n",size, getCapacity()));
  122. stringBuilder.append("front [");
  123. for (int i=front; i != tail; i = (i + 1)%data.length){
  124. stringBuilder.append(data[i]);
  125. if ((i + 1)%data.length != tail){
  126. stringBuilder.append(",");
  127. }
  128. }
  129. stringBuilder.append("] tail");
  130. return stringBuilder.toString();
  131. }
  132. }

循环队列测试类

  1. public class LoopQueueTest {
  2. public static void main(String[] args) {
  3. LoopQueue<Integer> integerArrayQueue = new LoopQueue<>();
  4. for (int i = 0; i < 10; i++) {
  5. integerArrayQueue.enqueue(i);
  6. System.out.println(integerArrayQueue);
  7.  
  8. if(i % 3 == 2){
  9. integerArrayQueue.dequeue();
  10. System.out.println(integerArrayQueue);
  11. }
  12. }
  13. }
  14. }
  1. //测试结果
  2. LoopQueue:size = 1, capacity = 5
  3. front [0] tail
  4. LoopQueue:size = 2, capacity = 5
  5. front [0,1] tail
  6. LoopQueue:size = 3, capacity = 5
  7. front [0,1,2] tail
  8. LoopQueue:size = 2, capacity = 5
  9. front [1,2] tail
  10. LoopQueue:size = 3, capacity = 5
  11. front [1,2,3] tail
  12. LoopQueue:size = 4, capacity = 5
  13. front [1,2,3,4] tail
  14. LoopQueue:size = 5, capacity = 5
  15. front [1,2,3,4,5] tail
  16. LoopQueue:size = 4, capacity = 5
  17. front [2,3,4,5] tail
  18. LoopQueue:size = 5, capacity = 5
  19. front [2,3,4,5,6] tail
  20. LoopQueue:size = 6, capacity = 10
  21. front [2,3,4,5,6,7] tail
  22. LoopQueue:size = 7, capacity = 10
  23. front [2,3,4,5,6,7,8] tail
  24. LoopQueue:size = 6, capacity = 10
  25. front [3,4,5,6,7,8] tail
  26. LoopQueue:size = 7, capacity = 10
  27. front [3,4,5,6,7,8,9] tail

测试结果是正确的,符合队列结构的数据存取,但基于自定义数组来实现,所以会调用数组方法的removeFirst方法,删除第一个元素的同时,会重新将后面所有元素前移,索引前移,均摊时间复杂度为O(n)。

3. 数组实现队列

  1. public class ArrayQueue<E> implements Queue<E>{
  2.  
  3. Array<E> array; //详情内容:https://www.cnblogs.com/FondWang/p/11806545.html
  4.  
  5. //初始化大小
  6. public ArrayQueue(int capacity){
  7. array=new Array<E>(capacity);
  8. }
  9.  
  10.   //无参构造器
  11. public ArrayQueue(){
  12. array=new Array<E>();
  13. }
  14.  
  15.    //入队。只能从队尾添加数据
  16. @Override
  17. public void enqueue(E param) {
  18. array.addLast(param);
  19. }
  20. //出队。只能从队首添加内容
  21. @Override
  22. public E dequeue() {
  23. return array.removeFirst();
  24. }
  25. //返回队首的元素
  26. @Override
  27. public E getFront() {
  28. return array.getFirst();
  29. }
  30.  
  31. @Override
  32. public int getSize() {
  33. return array.getSize();
  34. }
  35.  
  36. @Override
  37. public boolean isEmpty() {
  38. return array.isEmpty();
  39. }
  40.  
  41. @Override
  42. public String toString(){
  43. StringBuffer sb = new StringBuffer();
  44. sb.append("front: ");
  45. sb.append("[");
  46. for(int i=0;i<array.getSize();i++){
  47. sb.append(array.get(i));
  48. if(i!=array.getSize()-1){
  49. sb.append(", ");
  50. }
  51. }
  52. sb.append("] tail");
  53. return sb.toString();
  54. }
  55. }

数组队列测试类

  1. public class ArrayQueueTest {
  2. public static void main(String[] args) {
  3. ArrayQueue<Integer> integerArrayQueue = new ArrayQueue<>();
  4. for (int i = 0; i < 10; i++) {
  5. integerArrayQueue.enqueue(i);
  6. System.out.println(integerArrayQueue);
  7.  
  8. if(i % 3 == 2){
  9. integerArrayQueue.dequeue();
  10. System.out.println(integerArrayQueue);
  11. }
  12. }
  13. }
  14. }
  1. //测试结果
  2. ArrayQueue:front [0] tail
  3. ArrayQueue:front [0, 1] tail
  4. ArrayQueue:front [0, 1, 2] tail
  5. ArrayQueue:front [1, 2] tail
  6. ArrayQueue:front [1, 2, 3] tail
  7. ArrayQueue:front [1, 2, 3, 4] tail
  8. ArrayQueue:front [1, 2, 3, 4, 5] tail
  9. ArrayQueue:front [2, 3, 4, 5] tail
  10. ArrayQueue:front [2, 3, 4, 5, 6] tail
  11. ArrayQueue:front [2, 3, 4, 5, 6, 7] tail
  12. ArrayQueue:front [2, 3, 4, 5, 6, 7, 8] tail
  13. ArrayQueue:front [3, 4, 5, 6, 7, 8] tail
  14. ArrayQueue:front [3, 4, 5, 6, 7, 8, 9] tail
  1.  

因为引用了指针这个概念,删除的时候索引不会重排,均摊时间复杂度为O(1)

4. 循环队列和数组队列 效率对比

测试代码

  1. import java.util.Random;
  2. public class Main {
  3. private static double testQueue(Queue<Integer> q, int opCount){
  4. long startTime = System.nanoTime();
  5. Random random = new Random();
  6. for (int i=0;i<opCount; i++){
  7. q.enqueue(random.nextInt(Integer.MAX_VALUE));
  8. }
  9. for (int i=0; i<opCount;i++){
  10. q.dequeue();
  11. }
  12. long endTime = System.nanoTime();
  13. return (endTime - startTime) / 1000000000.0;
  14. }
  15.  
  16. public static void main(String[] args) {
  17. int opCount = 100000;//十万数据增删效率
  18.  
  19. ArrayQueue arrayQueue = new ArrayQueue();
  20. double time1 = testQueue(arrayQueue,opCount);
  21. System.out.println("ArrayQueue, time:" + time1 + "s");
  22.  
  23. LoopQueue loopQueue = new LoopQueue();
  24. double time2 = testQueue(loopQueue,opCount);
  25. System.out.println("LoopQueue, time:" + time2 + "s");
  26.  
  27. System.out.println("loopQueue队列数ArrayQueue的 " + Math.round(time1/time2) + "倍");
  28. }
  29. }

测试结果

  1. ArrayQueue, time:3.78317767s
  2. LoopQueue, time:0.011734084s
  3. loopQueue队列 ArrayQueue
  4.  
  5. //测试三次: 322、327、322,平均(322+327+323)/ 3 约为 323倍

数据结构 -- 队列Queue的更多相关文章

  1. 用go实现常用算法与数据结构——队列(queue)

    queue 简介 队列是一种非常常见的数据结构,日常生活中也能经常看到.一个典型的队列如下图(图片来自 segmentfault): 可以看出队列和我们日常生活中排队是基本一致的.都遵循 FIFO(F ...

  2. [C++][数据结构]队列(queue)的实现

    对于队列的定义,前人之述备矣. 队列的实现方法与栈非常相似.我直接在我实现的那个栈的代码上加了一点东西,全局替换了一些标识符,就实现了这个队列. 我实现的是一个queue<value>容器 ...

  3. 数据结构—队列(Queue)

    队列的定义--Queue 队列是只允许在表的队尾插入,在表的队头进行删除.队列具有先进先出的特性(FIFO, First In First Out). 队列提供了下面的操作 q.empty() 如果队 ...

  4. 数据结构-队列(Queue)

    #include <stdio.h> #include <stdlib.h> #define LIST_INIT_SIZE 10 #define LISTINCREMENT 1 ...

  5. Python与数据结构[2] -> 队列/Queue[0] -> 数组队列的 Python 实现

    队列 / Queue 数组队列 数组队列是队列基于数组的一种实现,其实现类似于数组栈,是一种FIFO的线性数据结构. Queue: <--| 1 | 2 | 3 | 4 | 5 |<-- ...

  6. java数据结构——队列、循环队列(Queue)

    每天进步一点点,坚持就是成功. 1.队列 /** * 人无完人,如有bug,还请斧正 * 继续学习Java数据结构————队列(列队) * 队列和栈一样,都是使用数组,但是队列多了一个队头,队头访问数 ...

  7. python基本数据结构栈stack和队列queue

    1,栈,后进先出,多用于反转 Python里面实现栈,就是把list包装成一个类,再添加一些方法作为栈的基本操作. 栈的实现: class Stack(object): #初始化栈为空列表 def _ ...

  8. 数据结构:队列queue 函数push() pop size empty front back

    队列queue: push() pop() size() empty() front() back() push()  队列中由于是先进先出,push即在队尾插入一个元素,如:可以输出:Hello W ...

  9. Java中的队列Queue,优先级队列PriorityQueue

    队列Queue 在java5中新增加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口. Queue使用时要尽量避免Collecti ...

随机推荐

  1. Robot Framework(十八) 支持工具

    5支持工具 5.1库文档工具(libdoc) libdoc是一种用于为HTML和XML格式的测试库和资源文件生成关键字文档的工具.前一种格式适用于人类,后者适用于RIDE和其他工具.Libdoc也没有 ...

  2. python3安装web.py

    今天准备测试代理池IPProxyPool获取到ip的质量,在安装web.py的时候遇到了些问题,在此记录一下. 1.安装资料 web.py官网:http://webpy.org/ web.py的git ...

  3. ROS Topic 常用指令

    rostopic list rosnode list一樣,就是列出目前運行中的topic有哪些. rostopic echo <topic_name> 接下來這個指令比較重要啦,就是去監聽 ...

  4. java基础篇之Object类

    1.Object类是所有类的超类 2.Object类的equals方法 public boolean equals(Object obj) {return (this == obj);} equals ...

  5. CTF辅助脚本

    首先推荐这篇文章,网上有多次转载,这是我见过日期比较早的 CTF中那些脑洞大开的编码和加密 凯撒密码 flag='flag{abcdef}' c='' n=20 for i in flag: if ' ...

  6. arcgis python 标注

    import arcpy mxd = arcpy.mapping.MapDocument("current") lyr = arcpy.mapping.ListLayers(mxd ...

  7. Restorator软件使exe文件都不能打开,exe不支持此接口

    遇到的问题: 下载了一个软件Restorator(资源修改器),填写完注册码之后,所有的exe文件都不能打开了,任务管理器不支持此接口打不开. 问题原因: 软件Restorator关联exe文件,运行 ...

  8. MySQL中information_schema数据库是干啥的

    大家在安装或使用MYSQL时,会发现除了自己安装的数据库以外,还有一个 information_schema数据库.information_schema数据库是做什么用的呢,使用WordPress博客 ...

  9. windows工程总结

    1.win32控制台console程序 运行在MS-DOS环境中的程序.控制台应用程序通常没有可视化的界面,只是通过字符串来显示或者监控程序.控制台程序常常被应用在测试.监控等用途,用户往往只关心数据 ...

  10. Python 数据库的Connection、Cursor两大对象

    Python 数据库的Connection.Cursor两大对象 pymysql是Python中操作MySQL的模块,其使用方法和py2的MySQLdb几乎相同. Python 数据库图解流程 Con ...