Java版-数据结构-队列(数组队列)

前言

看过笔者前两篇介绍的Java版数据结构数组的盆友,都给予了笔者一致的好评,在这里笔者感谢你们的承认!!!前端

因为本章介绍的数据结构是队列,在队列的实现上会基于前面写的动态数组来实现,而队列又和不管是从特色上和操做上都有相似之处,因此在这里对这两种数据结构不了解的朋友,能够去看一下笔者前两篇文章介绍的数据结构数组,这里笔者把连接贴出来(看过的盆友能够跳过此步骤...)java

介绍

队列是一种特殊的线性表,它只容许在表的前端(front)进行删除操做,而在表的后端(rear)进行插入操做,和栈同样,队列是一种操做受限制的线性表。进行插入操做的端称为队尾,进行删除操做的端称为队头。git

队列的操做方式和相似,惟一的区别在于队列只容许新数据在后端(rear)进行添加。github

特色

  • 队列是一种线性结构
  • 只能从一端(队尾)添加元素,从另外一端(队首)取出元素
  • 先进先出,First In First Out(FIFO)

以前在介绍栈的时候,经过示意图来帮助你们了解什么是栈;这里,我仍采用示意图形式向你们演示队列经常使用的两个操做:入队操做出队操做后端

队列入队操做数组

image-20190314004824471

这里咱们能够形象地想成咱们到银行办理业务排队的场景,如今A、B、C三个元素分别到银行柜台排成一条队办理业务(咱们都是文明的孩纸,总不能插队O(∩_∩)O哈!),依次排队的元素是:A、B、C。数据结构

队列出队操做ide

image-20190314004945601

当元素A办理完业务时,当前是元素A先离开队列,而后是元素B,最后是元素C性能

咱们时刻要牢记队列,入队是从队尾一端进行入队,出队是从队首一端进行出队,是一种:先进先出的数据结构。测试

本文会介绍队列的两张实现方式,一种是数组队列,另一种是循环队列,考虑篇幅长度缘由,本篇咱们暂时只介绍数组队列,循环队列放在下一篇介绍。

数组队列(底层基于数组实现

底层原理分析

如今咱们声明一个数组的长度(capacity=3),元素个数为(size=0)的int类型数组的空队列,在这里,假设对队列的队首为数组的左侧队尾为数组的右侧,示意图以下:

image-20190314023306672

如今若是咱们有四个元素:A、B、C、D要入队

元素A入队

image-20190314025039492

元素A已经入队了,如今开始元素B入队

image-20190314025109889

元素A和元素B已经入队了,如今开始元素C入队

image-20190314055125456

元素ABC已经分别入队了,如今若是咱们要开始元素D入队,根据咱们以前定义的动态数组的特性,若是元素D进行入队操做,会发现此时咱们的数组已经满了,这时候数组会自动地扩容(扩容的原理:新建一个容量是原数组容量两倍的数组,把原数组中的元素依次拷贝到新的数组中,最后引用指向新的数组)的原来的两倍(具体扩容多少,盆友能够自行设置)示意图以下:

image-20190314055438791

到这里咱们已经完成了元素:A、B、C、D的入队操做了,如今咱们来看一下,它们的出队操做,根据队列的特性,队列是一种先进先出的数据结构,以前入队操做顺序依次是:A->B->C->D,那么出队操做顺序仍然是:A->B->C->D

如今咱们来看一下元素A和元素B出队后的示意图:

image-20190314060105701

元素CD的出队原理和元素A出队的原理同样,直至所有出队完成,变成空队列

在元素出队的过程当中,相应地也会进行缩容操做,以前笔者这边定义,当数组中元素的个数(size)等于数组容量(capacity)的一半时,数组会进行缩容操做,这也正是动态数组的特色。

了解了数组队列的底层原理以后,接下来咱们用代码来实现一下(建议盆友,在看以前,本身能够尝试写一下,而后在看,这样印象可能会比较深入O(∩_∩)O哈!)

队列基本操做
  • 向队列中添加元素(入队)
void enqueue(E e);
复制代码
  • 从队列中取出元素(出队)
E dequeue();
复制代码
  • 获取队首元素
E getFront();
复制代码
  • 获取队列中元素个数
int getSize();
复制代码
  • 判断队列是否为空
boolean isEmpty();
复制代码
代码实现

接口定义Queue

public interface Queue<E> {
    /** * 入队 * * @param e */
    void enqueue(E e);

    /** * 出队 * * @return */
    E dequeue();

    /** * 获取队首元素 * * @return */
    E getFront();

    /** * 获取队列中元素的个数 * * @return */
    int getSize();

    /** * 判断队列是否为空 * * @return */
    boolean isEmpty();
}
复制代码

DynamicArrayQueue 类实现接口 Queue

public class DynamicArrayQueue<E> implements Queue<E> {

    /** * 用数组存放队列中元素的个数 */
    private DynamicArray<E> dynamicArray;

    /** * 指定容量,初始化队列 * * @param capacity */
    public DynamicArrayQueue(int capacity) {
        dynamicArray = new DynamicArray<>(capacity);
    }

    /** * 默认容量,初始化队列 */
    public DynamicArrayQueue() {
        dynamicArray = new DynamicArray<>();
    }


    @Override
    public void enqueue(E e) {
        dynamicArray.addLast(e);
    }

    @Override
    public E dequeue() {
        return dynamicArray.removeFirst();
    }

    @Override
    public E getFront() {
        return dynamicArray.getFirst();
    }

    @Override
    public int getSize() {
        return dynamicArray.getSize();
    }

    @Override
    public boolean isEmpty() {
        return dynamicArray.isEmpty();
    }

    @Override
    public String toString() {
        return "DynamicArrayQueue{" +
                "【队首】dynamicArray=" + dynamicArray + "}【队尾】";
    }
}
复制代码

测试类: DynamicArrayQueueTest

public class DynamicArrayQueueTest {
    @Test
    public void testArrayQueue() {
        // 指定容量(capacity=6)初始化队列
        DynamicArrayQueue<String> dynamicArrayQueue = new DynamicArrayQueue(3);
        System.out.println("初始队列:" + dynamicArrayQueue);

        // 准备入队元素
        List<String> enQueueElements = Arrays.asList("A", "B", "C");

        // 元素入队
        enQueueElements.forEach(x -> dynamicArrayQueue.enqueue(x));
        System.out.println("元素A、B、C入队:" + dynamicArrayQueue);

        // 此时若是又有一个元素D入队,会发生扩容操做 (size == capacity)进行扩容
        dynamicArrayQueue.enqueue("D");
        System.out.println("元素D入队,发生扩容:" + dynamicArrayQueue);


        // 元素A出队,会发生缩容操做(size == capacity / 2)进行缩容
        dynamicArrayQueue.dequeue();
        System.out.println("元素A出队,发生缩容:" + dynamicArrayQueue);

        // 元素B出队
        dynamicArrayQueue.dequeue();
        System.out.println("元素B出队:" + dynamicArrayQueue);

    }
}
复制代码

运行结果

初始队列:DynamicArrayQueue{【队首】dynamicArray=DynamicArray{data=[null, null, null], size=0,capacity=3}}【队尾】

元素A、B、C入队:DynamicArrayQueue{【队首】dynamicArray=DynamicArray{data=[A, B, C], size=3,capacity=3}}【队尾】

元素D入队,发生扩容:DynamicArrayQueue{【队首】dynamicArray=DynamicArray{data=[A, B, C, D, null, null], size=4,capacity=6}}【队尾】

元素A出队,发生缩容:DynamicArrayQueue{【队首】dynamicArray=DynamicArray{data=[B, C, D], size=3,capacity=3}}【队尾】

元素B出队:DynamicArrayQueue{【队首】dynamicArray=DynamicArray{data=[C, D, null], size=2,capacity=3}}【队尾】
复制代码

细心的盆友,会发现,由于队列的底层是数组来实现的,队列的出队操做实际上就是:删除数组中的第一个元素,后面的全部元素都要往前面挪一位;其实这样性能是比较低下的,时间复杂度是O(n)级别的。

咱们想若是元素进行出队操做后,可否不挪动后面的元素,还能维持队列的特性,这样问题不就解决了吗?盆友能够自行思考一下。

完整版代码GitHub仓库地址:Java版数据结构-队列(数组队列) 欢迎你们【关注】和【Star

本篇完成的数组队列是基于以前【Java版-数据结构-数组】动态数组来实现的,下一篇笔者会给你们介绍用循环队列来解决数组队列带来的性能问题。接下来,笔者还会一一的实现其它常见的数组结构。

  • 静态数组
  • 动态数组
  • 数组队列
  • 循环队列
  • 链表
  • 循环链表
  • 二分搜索树
  • 优先队列
  • 线段树
  • 字典树
  • AVL
  • 红黑树
  • 哈希表
  • ....

持续更新中,欢迎你们关注公众号:小白程序之路(whiteontheroad),第一时间获取最新信息!!!

相关文章
相关标签/搜索