定义

队的操作是在两端进行,一端只能进行插入操作(入队),称为队尾,一端只能进行删除操作(出队),称为队尾。

队列的运算规则是FIFO(first in first out)。

队列的入队、出队操作分别具有入队和出队的指针,通常以f(front) 表示队首指针,r(rear)表示队尾指针。

队列的存储具有顺序存储和链式存储两种方式。

基本运算:

  • 初始化;
  • 判断空;
  • 判断满;
  • 入队;
  • 出队;
  • 取出队首元素。
package com.wuwii.test;

/**
* 队列
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/17 13:12</pre>
*/
public interface Queue<E> {
/**
* 入队
* @param element 入队元素
*/
void enqueue(E element); /**
* 出队第一个元素
* @return 第一个元素
*/
E dequeue(); /**
* 返回队首的元素
* @return E 队首元素
*/
E frontValue(); /**
* 清空队列的所有元素
*/
void clear(); /**
* 判断队列是否为空
* @return true 为空,
*/
Boolean isEmpty(); }

顺序队列

。由于顺序存储结构的空间是连续的,需要一个向量空间存储当前队列的所有元素,由于队列的对头和队尾的位置是变化的。初始化的时候,rear 和front 都设置为 0 ;入队的时候将新元素插入rear 所指的位置,然后将rear 加 1 ;出队的时候,删除 front 所指的元素,然后将 front 加 1并返回该元素。由此可见,当头尾指针相等时队列为空。在非空队列中,头指针始终指向队头元素,而尾指针始终指向队尾元素的下个位置。

这里其实存在一个问题需要解决。队列的顺序存储结构同样也存在上溢和下溢的问题。而且,队列中还存在假溢出现象,指的是队列在入队和出队的操作中,头尾指针不断增加而不减少或者只减少而不增加,导致被删除的元素的空间无法重新利用,因此,尽管队列中实际的元素个数远远小于向量空间的规模,但也可能由于尾指针已远远超越向量空间的上界而不能进行入队或出队的操作,这种现象称为假上溢。

解决假上溢有多种方法。

  • 固定头指针:固定队首指针,一旦删除元素,需要移动所有元素,然后修改队尾指针,这样就又可以插入元素,只有在不能插入元素的时候,队列才是满的,否则可以一直插入元素,但是也存在一个,每次删除元素的时候,都移动了大量的数据,造成性能损失;
  • 循环向量:存储在循环向量的队列成为循环队列。

循环队列

存储在循环向量的队列成为循环队列

假设向量的空间为m,只要在入队或者出队操作的时候,将队首和队尾的指针对m做求模运算即可实现队首和队尾指针的循环,因此,队首和队尾指针的数值取值范围为0 ~ m-1 之间。

入队时:rear = (rear + 1) % maxSize;

出队时:front = (front + 1) % maxSize;

入队时,rear指针不断加一,知道rear 指针追上 front 指针的时候,队列满,rear = front,出队的时候,只需要判断 front = rear。因此,又出现了一个问题,判断队列的空与满的条件相同。

区分队列的空与满:

  • 方式一:浪费一个空间;rear指向刚刚插入的位置,front指向刚刚插入的位置;入队的时候先不修改rear 指针,先去判断(rear + 1) % maxSize = front,成立,表示队列已经满了。出队的时候只需要判断 front = rear,不为空的话,删除front = (front + 1) % maxSize位置的元素。
  • 方式二:设定一个变量来保存存储的元素个数;将该变量跟队列最大容量进行比较,如果相等,则队列满,如果该变量为0,表示队列为空。

上面两种方式都需要消费一个存储单元。

实现循环队列需要两个指针的开销,需要定义数组的最大下标范围,遵循先进先出原则。

package com.wuwii.test;

/**
* 顺序队列 -》循环队列
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/17 13:17</pre>
*/
public class Squeue<E> implements Queue<E> {
/**
* 队列默认大小
*/
private final int defaultSize = 10;
/**
* 队列存储的元素个数
*/
private int size;
/**
* 队首位置
*/
private int front;
/**
* 队尾位置
*/
private int rear;
/**
* 存储数组
*/
private Object[] elements; /**
* 无参构造,使用默认大小10
*/
public Squeue() {
initQueue(defaultSize);
} /**
* 有参构造,指定队列大小
* @param givenSize 需要指定队列大小
*/
public Squeue(int givenSize) {
initQueue(givenSize);
}
/**
* 入队
*
* @param element 入队元素
*/
@Override
public void enqueue(E element) {
checkFull();
rear = (rear + 1) % size;
elements[rear] = element;
} /**
* 出队第一个元素
*
* @return 第一个元素
*/
@Override
public E dequeue() {
checkEmpty();
front = (front + 1) % size;
return (E) elements[front];
} /**
* 返回队首的元素
*
* @return E 队首元素
*/
@Override
public E frontValue() {
return (E) elements[(front + 1) % size];
} /**
* 清空队列的所有元素
*/
@Override
public void clear() {
if (isEmpty()) {
return;
}
front = rear = 0;
} /**
* 判断队列是否为空
*
* @return true 为空,
*/
@Override
public Boolean isEmpty() {
return front == rear;
} /**
* 初始化队列
* @param givenSize 给定大小
*/
private void initQueue(int givenSize) {
size = givenSize + 1;
front = rear = 0;
elements = new Object[size];
} /**
* 检查队列满
* 队列满了抛出异常
*/
private void checkFull() {
if (front == (rear + 1) % size) {
throw new RuntimeException("Queue is full");
}
} /**
* 检查队列为空
* 为空抛出异常
*/
private void checkEmpty() {
if (isEmpty()) {
throw new RuntimeException("Queue is empty");
}
}
}

链式队列

相比顺序队列,链式队列的队首和队尾改为指针。

链式队列存在一个很方便的特点,只需要一个队尾指针就可以完成队列所有的基本运算,主要原因就是队列的运算方式时先进先出,已知一个任意节点,就可以查找出所有,所以只需要一个队尾就可以了,节约空间开销。

清空队列只需要将指针删除即可。

package com.wuwii.test;

/**
* 链式队列,循环队列
* @author Zhang Kai
* @version 1.0
* @since <pre>2017/12/17 14:14</pre>
*/
public class LinkQueue<E> implements Queue<E> {
/**
* 队首指针
*/
private Node<E> front;
/**
* 队尾指针
*/
private Node<E> rear; public LinkQueue() {
initQueue();
} /**
* 入队
*
* @param element 入队元素
*/
@Override
public void enqueue(E element) {
if (rear == null) {
rear = new Node<>(null, element);
} else {
front = rear = new Node<>(null, element);
}
} /**
* 出队第一个元素
*
* @return 第一个元素
*/
@Override
public E dequeue() {
checkEmpty();
E e = front.element;
front = front.next;
return e;
} /**
* 返回队首的元素
*
* @return E 队首元素
*/
@Override
public E frontValue() {
return front.element;
} /**
* 清空队列的所有元素
*/
@Override
public void clear() {
front = rear = null;
} /**
* 判断队列是否为空
*
* @return true 为空,
*/
@Override
public Boolean isEmpty() {
return rear == null;
} /**
* 存储单元,链表单元
* @param <E>
*/
private static class Node<E> {
Node<E> next;
E element;
Node(Node<E> next, E element) {
this.next = next;
this.element = element;
}
} /**
* 初始化队列
*/
private void initQueue() {
front = rear = null;
}
/**
* 检查队列为空
* 为空抛出异常
*/
private void checkEmpty() {
if (isEmpty()) {
throw new RuntimeException("Queue is empty");
}
}
}

Java中队列的更多相关文章

  1. Java 中队列同步器 AQS(AbstractQueuedSynchronizer)实现原理

    前言 在 Java 中通过锁来控制多个线程对共享资源的访问,使用 Java 编程语言开发的朋友都知道,可以通过 synchronized 关键字来实现锁的功能,它可以隐式的获取锁,也就是说我们使用该关 ...

  2. Java 中队列的使用

    刚才看见群里的一个朋友在问队列的使用,确实在现实的写代码中非常少使用队列的,反正我是从来没使用过.仅仅是学数据结构的时候学过. 以下是我写的一个小样例,希望有不足之处请提出改正.O(∩_∩)O~ 看代 ...

  3. java中队列Queue的使用

    1.在java5中新增加了java.util.Queue接口,用以支持队列的常见操作.Queue接口与List.Set同一级别,都是继承了Collection接口.Queue使用时要尽量避免Colle ...

  4. Java中常用七个阻塞队列的总结

    Java队列总结 通过前面文章的学习,我们对Java中常用队列做了介绍.本文,咱们来对队列做个总结吧. 首先,我们介绍了现实生活中的实际场景(排队买票等),来告诉我们为什么需要使用队列. 队列是一种先 ...

  5. JAVA中常见的阻塞队列详解

    在之前的线程池的介绍中我们看到了很多阻塞队列,这篇文章我们主要来说说阻塞队列的事. 阻塞队列也就是 BlockingQueue ,这个类是一个接 口,同时继承了 Queue 接口,这两个接口都是在JD ...

  6. Java Queue 队列

    队列是一种先进先出的数据结构,队列中插入元素和删除元素分别位于队列的两端. 在Java中 队列实现类众多,本文不再赘述.本文探讨的是如何自定义队列实现类: 基于数组方式实现队列: 注意点: 当出队时队 ...

  7. 聊聊并发(七)——Java中的阻塞队列

    3. 阻塞队列的实现原理 聊聊并发(七)--Java中的阻塞队列 作者 方腾飞 发布于 2013年12月18日 | ArchSummit全球架构师峰会(北京站)2016年12月02-03日举办,了解更 ...

  8. Java中的自定义数组队列

    在Java中,作为所有数据结构中存储和获取速度最快的一种,数组凭借其这种简单易用的优势在各个方面都能大显神威.但是数组也有自身的局限性.数组的长度必须是固定的一旦定义之后就无法动态的更改,这就会造成这 ...

  9. 延时队列:Java中的DelayQueue

    Java中的DelayQueue位于java.util.concurrent包下,本质是由PriorityQueue和BlockingQueue实现的阻塞优先级队列. 放入队列的元素需要实现java. ...

随机推荐

  1. Linux sar命令工具详细介绍

    sar命令工具详细介绍 by:授客 QQ:1033553122 由于篇幅限制,采用网盘分享, 下载地址: sar命令工具详细介绍.pdf

  2. 破解 jeb 2.3.7 demo

    前言 使用的技术和上文的一样. mips 版本的修改版 修改版: https://gitee.com/hac425/jeb-mips 正文 安卓版 jeb-2.3.7.201710262129-JEB ...

  3. 生成项目目录结构(based on windows system)

    描述: 作为程序员,在工作中,我们经常会有需求,需要罗列出项目的结构图:如果手工来整理的话,太过浪费时间,其实我们可以借助tree命令来快速生成目录结构. 本文主要介绍一下,基于windows系统,如 ...

  4. 前端模块化方案全解(CommonJS/AMD/CMD/ES6)

    模块化的开发方式可以提高代码复用率,方便进行代码的管理.通常一个文件就是一个模块,有自己的作用域,只向外暴露特定的变量和函数.目前流行的js模块化规范有CommonJS.AMD.CMD以及ES6的模块 ...

  5. Android批量打包提速 - 1分钟900个市场不是梦

    版权声明: 欢迎转载,但请保留文章原始出处 作者:GavinCT 出处:http://www.cnblogs.com/ct2011/p/4152323.html 黎明前的黑暗 使用Ant或者Gradl ...

  6. linux 挂载命令mount、umount

    mount /bin/mount语法:mount [-t文件系统] 设备文件名 挂载点mount -t iso9660 /dev/sr0 /mnt/cdromiso9660是固定的,光盘:所以 -t ...

  7. 6.Spring MVC SSM整合问题总结

    1.Cannot find class [org.springframework.http.converter.json.MappingJacksonHttpMessageConverter] for ...

  8. 关于easyUI一些标签的使用

    ①table: 1.class="easyui-datagrid":指定为easyUI的表格布局 2.pagination="true":带分页的表格 3.ro ...

  9. 关于Excel中的数据透视表没有数据

    在你想要使用数据透视表的时候,区域一定要正确 然后把你想要的数据按行列排好 如果没有数据 请点击刷新数据……刷新数据……刷新数据 我竟然被这个睿智的问题困扰好久……

  10. U-Mail:多方面因素避免EDM邮件进垃圾箱

    有很多做邮件营销的企业客户给U-Mail来电或来函咨询一件困扰他们的事:群发邮件时,要怎么样才能降低被收件人列入垃圾邮件的概率呢?其实关于这个问题,U-Mail小编已经请资深营销专家解答过多次了,经常 ...