前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >05-图解数据结构之队列--Queue

05-图解数据结构之队列--Queue

作者头像
张风捷特烈
发布2018-09-29 11:31:56
5950
发布2018-09-29 11:31:56
举报
零、前言

栈是一种线性的数据结构 特性:尾部添加,头部取出 即先进先出FIFO 操作:enqueue入队 dequeue出队 getFront查看队首元素

队列.png

一、队列接口
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/8/17 0017:15:57
 * 邮箱:1981462002@qq.com
 * 说明:队列接口
 */
public interface IQueue<T> {
    /**
     * 入队
     * @param el 元素
     */
    void enqueue(T el);

    /**
     * 出队
     * @return 元素
     */
    T dequeue();

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

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

    /**
     * 是否为空
     * @return 是否为空
     */
    boolean isEmpty();
}
二、普通队列的数组实现
代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/8/17 0017:15:57
 * 邮箱:1981462002@qq.com
 * 说明:队列的数组实现
 */
public class ArrayGroupQueue<E> implements IQueue<E> {

    /**
     * 成员变量
     */
    private ArrayGroup<E> array;

    public ArrayGroupQueue(int capacity) {
        this.array = new ArrayGroup<>(capacity);
    }

    public ArrayGroupQueue() {
        this.array = new ArrayGroup<>();
    }

    @Override
    public void enqueue(E el) {
        array.addLast(el);
    }

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

    @Override
    public E getFront() {
        return array.get(0);
    }

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

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

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("IQueue :");
        res.append("front [ ");
        for (int i = 0; i < array.size(); i++) {
            res.append(array.get(i));
            if (i != array.size() - 1) {
                res.append(", ");
            }
        }
        res.append("] tail");
        return res.toString();
    }

}
数组普通队列测试
代码语言:javascript
复制
 /**
  * 数组队列测试
  */
 private static void arrayQueueTest() {
     ArrayGroupQueue<Integer> queue = new ArrayGroupQueue<>();
     for (int i = 0; i < 5; i++) {
         queue.enqueue(i);
         System.out.println(queue);
     }
     queue.dequeue();
     System.out.println(queue);
     //IQueue :front [ 0] tail
     //IQueue :front [ 0, 1] tail
     //IQueue :front [ 0, 1, 2] tail
     //IQueue :front [ 0, 1, 2, 3] tail
     //IQueue :front [ 0, 1, 2, 3, 4] tail
     //IQueue :front [ 1, 2, 3, 4] tail
 }

三、循环队列

基于数组实现的队列在队首取出是会使得整队移动,而使时间复杂度为O(n) 可以使用循环队,基于队首队尾两个标示来固定队列,从而使得时间复杂度为O(1) 循环队列特点:为空时,队尾标示==队首标示,新加元素时,队尾表识后移, 队列满:(队尾标示+1)%数组长度==队首标示 循环队列会使队首前一个位置不可用。

循环队列.png

循环队列循环机制.png

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/8/17 0017:16:03
 * 邮箱:1981462002@qq.com
 * 说明:数组实现循环队列
 */
public class ArrayLoopQueue<T> implements IQueue<T> {
    /**
     * 队列数据
     */
    private T[] data;
    /**
     * 队首标示
     */
    private int front;
    /**
     * 队尾标示
     */
    private int tail;

    /**
     * 元素个数
     */
    private int size;

    /**
     * 无参构造:默认10个容量
     */
    public ArrayLoopQueue() {
        this(11);
    }

    /**
     * 一参构造
     *
     * @param capacity 队列容量
     */
    public ArrayLoopQueue(int capacity) {
        // 因为会有一个浪费,所以+1
        data = (T[]) new Object[capacity + 1];
        front = 0;
        tail = 0;
        size = 0;
    }

    @Override
    public void enqueue(T el) {
        if (isFull()) {
            grow(getCapacity() * 2);
        }
        data[tail] = el;
        //插入数据时对尾标示进行操作
        tail = (tail + 1) % data.length;
        size++;
    }

    @Override
    public T dequeue() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Cannot dequeue from an empty queue");
        }
        T ret = data[front];
        data[front] = null;
        //插入数据时对首标示进行操作
        front = (front + 1) % data.length;
        size--;
        if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
            grow(getCapacity() / 2);
        }
        return ret;
    }

    @Override
    public T getFront() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Cannot dequeue from an empty queue");
        }
        return data[front];
    }

    /**
     * 重新确定容量
     * @param newCapacity 新的容量
     */
    private void grow(int newCapacity) {
        T[] newData = (T[]) new Object[newCapacity + 1];
        for (int i = 0; i < size; i++) {
            // 此时在newData中队首对齐回来,data中就得有一个front的偏移量
            newData[i] = data[(i + front) % data.length];
        }
        data = newData;
        front = 0;
        tail = size;
    }

    /**
     * 获取容量
     * @return 容量
     */
    public int getCapacity() {
        return data.length - 1;
    }

    /**
     * 队列元素个数
     * @return 元素个数
     */
    @Override
    public int getSize() {
        return size;
    }

    /**
     * 是否为空
     * @return 是否为空
     */
    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    /**
     * 队列是否满了
     * @return 队列是否满了
     */
    public boolean isFull() {
        // tail的下一个位置等于front时
        return (tail + 1) % data.length == front;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append(String.format("ArrayLoopQueue: size = %d, capacity = %d\n", size, getCapacity()));
        res.append("front [");
        for (int i = front; i != tail; i = (i + 1) % data.length) {
            res.append(data[i]);
            if ((i + 1) % data.length != tail) // 最后一个元素不要加,
                res.append(", ");
        }
        res.append("] tail");
        return res.toString();
    }
}
测试:
代码语言:javascript
复制
/**
 * 循环队列测试
 */
private static void LoopQueueTest() {
    ArrayLoopQueue<Integer> queue = new ArrayLoopQueue<>(8);
    for (int i = 0; i < 8; i++) {
        queue.enqueue(i);
    }
    System.out.println(queue);
    //ArrayLoopQueue: size = 8, capacity = 8
    //front [0, 1, 2, 3, 4, 5, 6, 7] tail
    queue.dequeue();
    queue.dequeue();
    queue.dequeue();
    queue.dequeue();
    queue.enqueue(10);
    System.out.println(queue.getFront());//4
    System.out.println(queue);
    //ArrayLoopQueue: size = 5, capacity = 8
    //front [4, 5, 6, 7, 10] tail
}

四、链表式集合实现队列

链表和队列可谓天然配,链表的头删除,头获取都是O(1),但尾添加是O(n),但可以维护首位标识,使尾加也为O(1)

代码语言:javascript
复制
/**
 * 作者:张风捷特烈
 * 时间:2018/8/17 0017:22:50
 * 邮箱:1981462002@qq.com
 * 说明:单链表实现队列
 */
public class SingleLinkedQueue<T> implements IQueue<T> {

    /**
     * 头节点
     */
    private Node head;
    /**
     * 尾节点
     */
    private Node tail;

    /**
     * 元素个数
     */
    private int size;

    public SingleLinkedQueue() {
        head = null;
        tail = null;
        size = 0;
    }

    @Override
    public void enqueue(T el) {
        // 如果队尾为空,说明队列是空的。因为tail一直指向最后一个非空节点。
        if (tail == null) {
            tail = new Node(null, el);
            head = tail;
        } else {
            // 使用tail.next把新Node挂载上来。
            tail.next = new Node(null, el);
            // tail后挪
            tail = tail.next;
        }
        size++;
    }

    @Override
    public T dequeue() {
        if (isEmpty())
            throw new IllegalArgumentException("Cannot dequeue from an empty queue.");

        Node targetNode = head;
        head = head.next; // head后移
        targetNode.next = null; // 元素置空
        if (head == null) {// 如果头结点为空
            tail = null;
        }

        size--;
        return targetNode.el;
    }

    @Override
    public T getFront() {
        if (isEmpty())
            throw new IllegalArgumentException("IQueue is empty.");
        return head.el;
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return size == 0;
    }

    @Override
    public String toString() {
        StringBuilder res = new StringBuilder();
        res.append("IQueue: front ");

        Node cur = head;
        while (cur != null) {
            res.append(cur + "->");
            cur = cur.next;
        }
        res.append("NULL tail");
        return res.toString();
    }


    private class Node {
        public T el;//改节点上的元素
        public Node next; //下一节点


        /**
         * 两参构造
         *
         * @param next //下一节点
         * @param el    生成节点的元素值
         */
        public Node(Node next, T el) {
            this.el = el;
            this.next = next;
        }

        @Override
        public String toString() {
            return el.toString();
        }
    }
}

四、时间测试
数组普通队列:ArrayGroupQueue测试

方法\数量

复杂度

1000

次10000次

10W次

100W次

1000次

enqueue

O(1)

0.0006秒

0.0022秒

0.01571秒

0.06668秒

1.1375秒

dequeue

O(n)

0.0111秒

0.2707秒

18.7684秒

----

--

getFront

O(1)

--

--

--

--

--

数组环形队列:ArrayLoopQueue测试

方法\数量

复杂度

1000

次10000次

10W次

100W次

1000次

enqueue

O(1)

0.0004秒

0.0019秒

0.01775秒

0.05414秒

0.6896秒

dequeue

O(1)

0.0005秒

0.0021秒

0.0091秒

0.0360秒

0.3327秒

getFront

O(1)

--

--

--

--

--

链表队列:SingleLinkedStack测试

方法\数量

复杂度

1000

次10000次

10W次

100W次

1000次

enqueue

O(1)

0.0011秒

0.0031秒

0.0099秒

0.4881秒

3.1186秒

dequeue

O(1)

0.0002秒

0.0013秒

0.0046秒

0.0221秒

0.1388秒

getFront

O(1)

--

--

--

--

--


后记、
1.声明:

[1]本文由张风捷特烈原创,各图均由本人亲自所画,转载请注明 [2]欢迎广大编程爱好者共同交流 [3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正 [4]你的喜欢与支持将是我最大的动力

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2018.09.24 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 零、前言
  • 一、队列接口
  • 二、普通队列的数组实现
    • 数组普通队列测试
    • 三、循环队列
      • 测试:
      • 四、链表式集合实现队列
      • 四、时间测试
        • 数组普通队列:ArrayGroupQueue测试
          • 数组环形队列:ArrayLoopQueue测试
            • 链表队列:SingleLinkedStack测试
            • 后记、
              • 1.声明:
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档