数据结构 -- 队列Queue
一、队列简介

定义
队列(queue)在计算机科学中,是一种先进先出的线性表。 它只允许在表的前端进行删除操作,而在表的后端进行插入操作。进行插入操作的端称为队尾,进行删除操作的端称为队头。队列中没有元素时,称为空队列。
1. 队列是一种线性结构;
2. 相比数组,队列对应操作的是数组的子集;
3. 只能从一端(队尾)添加元素,只能从另一端(队首)取出元素 。先进先出的数据结构(先到先得First In First Out【FIFO】)。
二、代码实现
1. 队列接口
public interface Queue{
int getSize(); //返回元素的个数
E getFront(); //返回队首元素内容
boolean isEmpty(); //判断是否为空
void enqueue(E e); // 入队
E dequeue(); //出队
}
2、循环队列
循环队列中有两个新词,两个指针
- front 指向队列的第一个元素,初始指向0
- tail 指向队列的最后一个元素的后一个位置,初始指向0
- 循环队列就是将队列存储空间的最后一个位置绕到第一个位置,形成逻辑上的环状空间,供队列循环使用。在循环队列结构中,当存储空间的最后一个位置已被使用而再要进入队运算时,只需要存储空间的第一个位置空闲,便可将元素加入到第一个位置,即将存储空间的第一个位置作为队尾。 [1] 循环队列可以更简单防止伪溢出的发生,但队列大小是固定的。
public class LoopQueue<E> implements Queue<E> {
private E[] data;
//指向队列的第一个元素,初始指向0
private int front;
//指向队列的最后一个元素的后一个位置,初始指向0
private int tail;
//元素数量
private int size;
public LoopQueue(int capacity){
data = (E[]) new Object[capacity + 1];
front = 0;
tail = 0;
size = 0;
}
public LoopQueue(){
this(10);
}
@Override
public int getSize() {
return size;
}
/**
* 因为容量放的时候多了个1,所以get容量的时候,需要减1
* @return
*/
public int getCapacity(){
return data.length - 1;
}
/**
* 当front和tail的值相等时,队列为空,初始两个指向的是同一个值(只有初始的时候,指向的是同一个地方)
* @return
*/
@Override
public boolean isEmpty() {
return front == tail;
}
/**
* 1.if((tail + 1) % data.length == front) 如果tail + 1 超过了data.length的大小,
* 代表当前tail指向已经超出了容量的大小,因为是循环式,所以需要tail去循环头元素中查看值是否有被占用,
* 如果 == front 代表循环头没有,就需要扩容了。
* 2.举例: 元素容量为8,tail目前指向7 front 指向2
* if((7 + 1) % 8 == 2 ) if(0 == 2) 这里是false,因为front指向了2,所以代表 第0,1位是没有值的
* 所以这个值需要在在第0位放(空间利用)
* 3.data[tail] = param tail当前指向的地方需要赋值,然后tail自增 循环体 的1,size+1
* @param param
*/
@Override
public void enqueue( E param) {
if ((tail + 1) % data.length == front) {
resize(getCapacity() * 2);
}
data[tail] = param;
tail = (tail + 1) % data.length;
size++;
}
/**
* 1.如果队列为空抛出异常
* 2.用ret变量来接受当前队列头的值
* 3.接收成功之后将,队列头元素置空
* 4.front指针指向下一个元素
* 5.size大小-1
* 6.如果size大小占据了容量的1/4和size为容量的1/2且不等于0的时候,对容量进行缩减,缩减为原来容量的1/2
* 7.返回ret变量
* @return
*/
@Override
public E dequeue() {
if(isEmpty()){
throw new IllegalArgumentException("Cannot dequeue from an empty queue");
}
E ret = data[front];
data[front] = null;
front = (front + 1) % data.length;
size --;
if (size == getCapacity() / 4 && getCapacity() / 2 != 0){
resize(getCapacity()/2);
}
return ret;
}
@Override
public E getFront() {
if (isEmpty())
throw new IllegalArgumentException("Queue is empty");
return data[front];
}
/**
* 扩充队列的容量
* 1.front代表了当前元素初始位置的指向
* 2.newData的第i位元素,应该等于 i + front % data.length 的值
* 3.举例:元素容量20,i 等于 0 ,front 等于 2,结果: newData[0] = data[(0 + 2) % 20]
* = data[2] 意思就是,newData的第一位元素,应该等于data有值的第一位元素
* % data.length 的原因主要是为了防止数组越界错误
* 4.新数组赋值完成需要将 front 重新指向0,因为新数组的front指针是从0开始的。
* tail最后要指向等于size大小的值,
* @param newCapacity
*/
private void resize(int newCapacity){
E[] newData = (E[]) new Object[newCapacity + 1];
for (int i=0; i < size; i++){
newData[i] = data[(i + front) % data.length];
}
data = newData;
front = 0;
tail = size;
}
/**
* 1.元素从 front位置开始循环遍历,i的值不能等于tail,
* 也就是到tail的前一位,i = i + 1 且%data.length,
* 因为i有可能从循环头重新开始
* 2.( i + 1 ) % data.length != tail 如果当前i + 1 % data.length
* 不等于tail表示不到最后一个元素,就拼接,
* @return
*/
@Override
public String toString(){
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(String.format("LoopQueue:size = %d, capacity = %d\n",size, getCapacity()));
stringBuilder.append("front [");
for (int i=front; i != tail; i = (i + 1)%data.length){
stringBuilder.append(data[i]);
if ((i + 1)%data.length != tail){
stringBuilder.append(",");
}
}
stringBuilder.append("] tail");
return stringBuilder.toString();
}
}
循环队列测试类
public class LoopQueueTest {
public static void main(String[] args) {
LoopQueue<Integer> integerArrayQueue = new LoopQueue<>();
for (int i = 0; i < 10; i++) {
integerArrayQueue.enqueue(i);
System.out.println(integerArrayQueue);
if(i % 3 == 2){
integerArrayQueue.dequeue();
System.out.println(integerArrayQueue);
}
}
}
}
//测试结果
LoopQueue:size = 1, capacity = 5
front [0] tail
LoopQueue:size = 2, capacity = 5
front [0,1] tail
LoopQueue:size = 3, capacity = 5
front [0,1,2] tail
LoopQueue:size = 2, capacity = 5
front [1,2] tail
LoopQueue:size = 3, capacity = 5
front [1,2,3] tail
LoopQueue:size = 4, capacity = 5
front [1,2,3,4] tail
LoopQueue:size = 5, capacity = 5
front [1,2,3,4,5] tail
LoopQueue:size = 4, capacity = 5
front [2,3,4,5] tail
LoopQueue:size = 5, capacity = 5
front [2,3,4,5,6] tail
LoopQueue:size = 6, capacity = 10
front [2,3,4,5,6,7] tail
LoopQueue:size = 7, capacity = 10
front [2,3,4,5,6,7,8] tail
LoopQueue:size = 6, capacity = 10
front [3,4,5,6,7,8] tail
LoopQueue:size = 7, capacity = 10
front [3,4,5,6,7,8,9] tail
测试结果是正确的,符合队列结构的数据存取,但基于自定义数组来实现,所以会调用数组方法的removeFirst方法,删除第一个元素的同时,会重新将后面所有元素前移,索引前移,均摊时间复杂度为O(n)。
3. 数组实现队列
public class ArrayQueue<E> implements Queue<E>{
Array<E> array; //详情内容:https://www.cnblogs.com/FondWang/p/11806545.html
//初始化大小
public ArrayQueue(int capacity){
array=new Array<E>(capacity);
}
//无参构造器
public ArrayQueue(){
array=new Array<E>();
}
//入队。只能从队尾添加数据
@Override
public void enqueue(E param) {
array.addLast(param);
}
//出队。只能从队首添加内容
@Override
public E dequeue() {
return array.removeFirst();
}
//返回队首的元素
@Override
public E getFront() {
return array.getFirst();
}
@Override
public int getSize() {
return array.getSize();
}
@Override
public boolean isEmpty() {
return array.isEmpty();
}
@Override
public String toString(){
StringBuffer sb = new StringBuffer();
sb.append("front: ");
sb.append("[");
for(int i=0;i<array.getSize();i++){
sb.append(array.get(i));
if(i!=array.getSize()-1){
sb.append(", ");
}
}
sb.append("] tail");
return sb.toString();
}
}
数组队列测试类
public class ArrayQueueTest {
public static void main(String[] args) {
ArrayQueue<Integer> integerArrayQueue = new ArrayQueue<>();
for (int i = 0; i < 10; i++) {
integerArrayQueue.enqueue(i);
System.out.println(integerArrayQueue);
if(i % 3 == 2){
integerArrayQueue.dequeue();
System.out.println(integerArrayQueue);
}
}
}
}
//测试结果
ArrayQueue:front [0] tail
ArrayQueue:front [0, 1] tail
ArrayQueue:front [0, 1, 2] tail
ArrayQueue:front [1, 2] tail
ArrayQueue:front [1, 2, 3] tail
ArrayQueue:front [1, 2, 3, 4] tail
ArrayQueue:front [1, 2, 3, 4, 5] tail
ArrayQueue:front [2, 3, 4, 5] tail
ArrayQueue:front [2, 3, 4, 5, 6] tail
ArrayQueue:front [2, 3, 4, 5, 6, 7] tail
ArrayQueue:front [2, 3, 4, 5, 6, 7, 8] tail
ArrayQueue:front [3, 4, 5, 6, 7, 8] tail
ArrayQueue:front [3, 4, 5, 6, 7, 8, 9] tail
因为引用了指针这个概念,删除的时候索引不会重排,均摊时间复杂度为O(1)
4. 循环队列和数组队列 效率对比
测试代码
import java.util.Random;
public class Main {
private static double testQueue(Queue<Integer> q, int opCount){
long startTime = System.nanoTime();
Random random = new Random();
for (int i=0;i<opCount; i++){
q.enqueue(random.nextInt(Integer.MAX_VALUE));
}
for (int i=0; i<opCount;i++){
q.dequeue();
}
long endTime = System.nanoTime();
return (endTime - startTime) / 1000000000.0;
} public static void main(String[] args) {
int opCount = 100000;//十万数据增删效率 ArrayQueue arrayQueue = new ArrayQueue();
double time1 = testQueue(arrayQueue,opCount);
System.out.println("ArrayQueue, time:" + time1 + "s"); LoopQueue loopQueue = new LoopQueue();
double time2 = testQueue(loopQueue,opCount);
System.out.println("LoopQueue, time:" + time2 + "s"); System.out.println("loopQueue队列数ArrayQueue的 " + Math.round(time1/time2) + "倍");
}
}
测试结果
ArrayQueue, time:3.78317767s
LoopQueue, time:0.011734084s
loopQueue队列 是ArrayQueue 的 倍 //测试三次: 322、327、322,平均(322+327+323)/ 3 约为 323倍
数据结构 -- 队列Queue的更多相关文章
- 用go实现常用算法与数据结构——队列(queue)
queue 简介 队列是一种非常常见的数据结构,日常生活中也能经常看到.一个典型的队列如下图(图片来自 segmentfault): 可以看出队列和我们日常生活中排队是基本一致的.都遵循 FIFO(F ...
- [C++][数据结构]队列(queue)的实现
对于队列的定义,前人之述备矣. 队列的实现方法与栈非常相似.我直接在我实现的那个栈的代码上加了一点东西,全局替换了一些标识符,就实现了这个队列. 我实现的是一个queue<value>容器 ...
- 数据结构—队列(Queue)
队列的定义--Queue 队列是只允许在表的队尾插入,在表的队头进行删除.队列具有先进先出的特性(FIFO, First In First Out). 队列提供了下面的操作 q.empty() 如果队 ...
- 数据结构-队列(Queue)
#include <stdio.h> #include <stdlib.h> #define LIST_INIT_SIZE 10 #define LISTINCREMENT 1 ...
- Python与数据结构[2] -> 队列/Queue[0] -> 数组队列的 Python 实现
队列 / Queue 数组队列 数组队列是队列基于数组的一种实现,其实现类似于数组栈,是一种FIFO的线性数据结构. Queue: <--| 1 | 2 | 3 | 4 | 5 |<-- ...
- java数据结构——队列、循环队列(Queue)
每天进步一点点,坚持就是成功. 1.队列 /** * 人无完人,如有bug,还请斧正 * 继续学习Java数据结构————队列(列队) * 队列和栈一样,都是使用数组,但是队列多了一个队头,队头访问数 ...
- python基本数据结构栈stack和队列queue
1,栈,后进先出,多用于反转 Python里面实现栈,就是把list包装成一个类,再添加一些方法作为栈的基本操作. 栈的实现: class Stack(object): #初始化栈为空列表 def _ ...
- 数据结构:队列queue 函数push() pop size empty front back
队列queue: push() pop() size() empty() front() back() push() 队列中由于是先进先出,push即在队尾插入一个元素,如:可以输出:Hello W ...
- Java中的队列Queue,优先级队列PriorityQueue
队列Queue 在java5中新增加了java.util.Queue接口,用以支持队列的常见操作.该接口扩展了java.util.Collection接口. Queue使用时要尽量避免Collecti ...
随机推荐
- java枚举类型总结
java中的枚举类型是jdk1.5新增的一个东西,其本质是一个java.lang.Enum类的子类,每个枚举项是一个静态常量对象,由编译器为每个枚举项分配ordinal和name,其中ordinal是 ...
- bbs-admin-自定义admin(二)
本文内容 目的:模仿admin默认配置,自定义配置类 一 查 1 查看数据 2 查看表头 3 分页器 4 search(搜索框) 5 action(批量处理) 6 filter(分类) ...
- 原来INF文件是干这个的
When the drivers for a device are installed, the installer uses information in an information (INF) ...
- 1.1 OC class new summary
1.http://www.cnblogs.com/mjios/archive/2013/04/06/3002814.html 2.How to create a oc class? 3. 3.1 In ...
- Nginx中配置非英文域名
前两天遇到个配置越南语的域名的情况.域名和ip解析完成后,直接ping域名也不通,还以为是解析问题.研究了半天,nginx配置非英文域名时,需要有其他操作. 非英文域名转换成punycode编码才可以 ...
- vue props传值后watch事件未触发的问题
父组件传值,子组件监听,明明很简单的一个事情,硬是卡了许久(毕竟不是专业搞前端的,还是吃亏在学识浅陋).也和自己钻牛角尖有关,想自己解决问题. 早期我写过一篇vue组件传值的文章,传值方式是这样的: ...
- Linux下使用 Nginx
1. 下载Nginx 下载地址 下载完成后,上传到linux服务器,我上传到了 /opt文件夹下. 2. 安装Nginx前的准备 要想使用Nginx需要安装PCRE库和zlib库,否则直接安装Ngin ...
- --thunder-lock is available since uWSGI 1.4.6 but never got documentation (of any kind)
--thunder-lock is available since uWSGI 1.4.6 but never got documentation (of any kind) Serializing ...
- centos中screen的使用
只是讲解几个简单的操作,例如,创建新窗口,切换到新窗口,删除窗口.(因为我要启动elk,所以需要使用到screen) 一.创建一个新窗口: 安装完成后,直接敲命令screen就可以启动它.但是这样启动 ...
- Server 2012使用Windows PowerShell cmdlet安装角色和角色服务功能
Server 2012使用Windows PowerShell cmdlet安装角色和角色服务功能 Windows Server 2012 安装 SQL server 2008 出现了如下错误: 解 ...