定义

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

队列的运算规则是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. Portal的安全代理(反向代理出口)配置架构

    对于正式运行的环境,一般需要设置网络安全控制区DMZ,通过代理,把仅需要的端口向客户端暴露,其他内部端口应该是在防火墙包含之内的. 下文将针对ArcGIS 的Portal软件,讲述在DMZ中如何架构的 ...

  2. private 与 super

    public class Person { private String name; private int age; } public class Student extends Person { ...

  3. Oracle获取session的trace

    1. 使用参数SQL_TRACE 下面是官网对此参数的说明 SQL_TRACE Property Description Parameter type Boolean Default value fa ...

  4. 指令-Directive

    restrict:'A'用作设定用那种方式使用指令. 可组合使用如restrict:'AE' E - 元素名称: <my-directive></my-directive> A ...

  5. 国内一元钱 正常搭建android开发环境

    如果你人在gfw之外,那么此篇文章对你来说毫无用处,请自动略过.. 笔者自android出来之后,就一直想尝试一下.可惜,几年来一直未能够定下身心来研究尝试.而所做的工作也与android没有关系,所 ...

  6. Jboss的jmx-console中查看内存和线程状态

    步骤: 1.假设jboss运行在 192.168.1.100:8080 地址和端口上. 2. 浏览器中访问http://192.168.1.100:8080/,然后选择jmx-console 3.选择 ...

  7. [swift] Async

    Async https://github.com/duemunk/Async Syntactic sugar in Swift for asynchronous dispatches in Grand ...

  8. [UI] 精美UI界面欣赏[12]

    精美UI界面欣赏[12]

  9. iOS7中UIView的animateKeyframesWithDuration方法讲解

    iOS7中UIView的animateKeyframesWithDuration方法讲解 在iOS7中,给UIView添加了一个方法用来直接使用关键帧动画而不用借助CoreAnimation来实现,那 ...

  10. lua调用c++函数返回值作用

    2015/05/28 lua调用c++接口,返回给lua函数的是压入栈的内容,可以有多个返回值.但是c++接口本身也是有返回值的,这个返回值也非常的重要,会决定最后返回到lua函数的值的个数. (1) ...