1.. 队列基础
  • 队列也是一种线性结构;
  • 相比数组,队列所对应的操作数是队列的子集;
  • 队列只允许从一端(队尾)添加元素,从另一端(队首)取出元素;
  • 队列的形象化描述如下图:
  • 队列是一种先进先出(First In First Out)的数据结构;
2.. 队列的实现
  • 任务目标如下:
  • Queue<E>
    ·void enqueue(E) //入队
    ·E dequeue() //出队
    ·E getFront() //查看队首元素
    ·int getSize() //查看队列中元素的个数
    ·boolean isEmpty() //查看队列是否为空
  • 需要提一下,从用户的角度来看,只要实现上述操作就好,具体底层实现,用户并不关心,实际上,底层确实有多种实现方式。
  • 我们准备在之前实现的动态数组基础上,来实现"队列"这种数据结构。
  • 先定义一个接口Interface,如下:
  • public interface Queue<E> {
    int getSize(); boolean isEmpty(); void enqueue(E e); E dequeue(); E getFront(); }
  • 实现基于Array类的ArrayQueue类,并进行测试:
  • public class ArrayQueue<E> implements Queue<E> {
    private Array<E> array; //构造函数
    public ArrayQueue(int capacity) {
    array = new Array<>(capacity);
    } //无参数构造函数
    public ArrayQueue() {
    array = new Array<>();
    } //实现getSize()方法
    @Override
    public int getSize() {
    return array.getSize();
    } //实现isEmpty方法
    @Override
    public boolean isEmpty() {
    return array.isEmpty();
    } //实现getCapacity方法
    public int getCapacity() {
    return array.getCapacity();
    } //实现enqueue方法
    @Override
    public void enqueue(E e) {
    array.addLast(e);
    } //实现dequeue方法
    @Override
    public E dequeue() {
    return array.removeFirst();
    } //实现getFront方法
    @Override
    public E getFront() {
    return array.getFirst();
    } //方便打印测试
    @Override
    public String toString() {
    StringBuilder res = new StringBuilder();
    res.append("Queue: ");
    res.append("front [");
    for (int i = ; i < array.getSize(); i++) {
    res.append(array.get(i));
    if (i != array.getSize() - ) {
    res.append(", ");
    }
    }
    res.append("] tail");
    return res.toString();
    } // 测试
    public static void main(String[] args){
    ArrayQueue<Integer> queue = new ArrayQueue<>(); // 测试入队
    for(int i=;i<;i++){
    queue.enqueue(i);
    }
    System.out.println(queue); // 测试出队
    queue.dequeue();
    System.out.println(queue);
    }
    }
  • 输出结果:
  • Queue: front [, , , , ] tail
    Queue: front [, , , ] tail

3.. 数组队列的时间复杂度分析:

  • ArrayQueue<E>
    ·void enqueue(E) O() 均摊
    ·E dequeue() O(n)
    ·E getFront() O()
    ·int getSize() O()
    ·boolean isEmpty() O()
4.. 循环队列
  • 数组队列的出队操作的复杂度是O(n),性能很差,解决方法就是使用循环队列(Loop Queue)
  • 循环队列的示意图如下:
  • 实现循环队列的业务逻辑,并进行测试:
  • public class LoopQueue<E> implements Queue<E> {
    
        private E[] data;
    private int front, tail;
    private int size; //构造函数
    public LoopQueue(int capacity) {
    data = (E[]) new Object[capacity + ];
    front = ;
    tail = ;
    size = ;
    } //无参数构造函数
    public LoopQueue() {
    this(); //直接调用有参数的构造函数,然后传入一个默认值
    } //实现getCapacity方法
    public int getCapacity() {
    return data.length - ;
    } //实现isEmpty方法
    @Override
    public boolean isEmpty() {
    return front == tail;
    } //实现getSize方法
    @Override
    public int getSize() {
    return size;
    } //实现enqueue方法
    @Override
    public void enqueue(E e) {
    //判断队列是否已满
    if ((tail + ) % data.length == front) {
    resize(getCapacity() * );
    } data[tail] = e;
    tail = (tail + ) % data.length;
    size++;
    } //实现dequeue方法
    @Override
    public E dequeue() {
    //判断队列是否为空
    if (isEmpty()) {
    throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
    } E ret = data[front];
    data[front] = null;
    front = (front + ) % data.length;
    size--; if (size == getCapacity() / && getCapacity() / != ) {
    resize(getCapacity() / );
    }
    return ret;
    } //实现getFront方法
    @Override
    public E getFront() {
    if (isEmpty()) {
    throw new IllegalArgumentException("Queue is empty.");
    }
    return data[front];
    } //实现resize方法
    private void resize(int newCapacity) {
    E[] newData = (E[]) new Object[newCapacity + ];
    for (int i = ; i < size; i++) {
    newData[i] = data[(i + front) % data.length];
    }
    data = newData;
    front = ;
    tail = size;
    } //方便打印测试
    @Override
    public String toString() {
    StringBuilder res = new StringBuilder();
    res.append(String.format("Queue: size=%d, capacity=%d\n", size, getCapacity()));
    res.append("front [");
    for (int i = front; i != tail; i = (i + ) % data.length) {
    res.append(data[i]);
    if ((i + ) % data.length != tail) {
    res.append(", ");
    }
    }
    res.append("] tail");
    return res.toString();
    } //测试
    public static void main(String[] args) {
    LoopQueue<Integer> queue = new LoopQueue<>(); // 测试入队
    for (int i = ; i < ; i++) {
    queue.enqueue(i);
    }
    System.out.println(queue); // 测试出队
    queue.dequeue();
    System.out.println(queue);
    }
    }
  • 输出结果:
  • Queue: size=, capacity=
    front [, , , , ] tail
    Queue: size=, capacity=
    front [, , , ] tail

5.. 循环队列的复杂度分析

  • LoopQueue<E>
    ·void enqueue(E) O() 均摊
    ·E dequeue() O() 均摊
    ·E getFront() O()
    ·int getSize() O()
    ·boolean isEmpty() O()

6.. 使用简单算例测试ArrayQueue与LoopQueue的性能差异

  • import java.util.Random;
    
    public class Main {
    
        // 测试使用q运行opCount个enqueue和dequeue操作所需要的时间,单位:秒
    private static double testQueue(Queue<Integer> q, int opCount) {
    long startTime = System.nanoTime(); Random random = new Random();
    for (int i = ; i < opCount; i++) {
    q.enqueue(random.nextInt(Integer.MAX_VALUE));
    }
    for (int i = ; i < opCount; i++) {
    q.dequeue();
    } long endTime = System.nanoTime();
    return (endTime - startTime) / 1000000000.0;
    } public static void main(String[] args) { int opCount = ; ArrayQueue<Integer> arrayQueue = new ArrayQueue<>();
    double time1 = testQueue(arrayQueue, opCount);
    System.out.println("ArrayQueue, time: " + time1 + " s"); LoopQueue<Integer> loopQueue = new LoopQueue<>();
    double time2 = testQueue(loopQueue, opCount);
    System.out.println("LoopQueue, time: " + time2 + " s"); }
    }
  • 输出结果
  • ArrayQueue, time: 2.88077896 s
    LoopQueue, time: 0.01140229 s

第二十四篇 玩转数据结构——队列(Queue)的更多相关文章

  1. 第二十八篇 玩转数据结构——堆(Heap)和有优先队列(Priority Queue)

          1.. 优先队列(Priority Queue) 优先队列与普通队列的区别:普通队列遵循先进先出的原则:优先队列的出队顺序与入队顺序无关,与优先级相关. 优先队列可以使用队列的接口,只是在 ...

  2. 第二十六篇 玩转数据结构——二分搜索树(Binary Search Tree)

          1.. 二叉树 跟链表一样,二叉树也是一种动态数据结构,即,不需要在创建时指定大小. 跟链表不同的是,二叉树中的每个节点,除了要存放元素e,它还有两个指向其它节点的引用,分别用Node l ...

  3. 第二十五篇 玩转数据结构——链表(Linked List)

          1.. 链表的重要性 我们之前实现的动态数组.栈.队列,底层都是依托静态数组,靠resize来解决固定容量的问题,而"链表"则是一种真正的动态数据结构,不需要处理固定容 ...

  4. 第三十四篇 玩转数据结构——哈希表(HashTable)

    1.. 整型哈希函数的设计 小范围正整数直接使用 小范围负整数整体进行偏移 大整数,通常做法是"模一个素数"   2.. 浮点型哈希函数的设计 转成整型进行处理   3.. 字符串 ...

  5. 第二十九篇 玩转数据结构——线段树(Segment Tree)

          1.. 线段树引入 线段树也称为区间树 为什么要使用线段树:对于某些问题,我们只关心区间(线段) 经典的线段树问题:区间染色,有一面长度为n的墙,每次选择一段墙进行染色(染色允许覆盖),问 ...

  6. SpringBoot第二十四篇:应用监控之Admin

    作者:追梦1819 原文:https://www.cnblogs.com/yanfei1819/p/11457867.html 版权声明:本文为博主原创文章,转载请附上博文链接! 引言   前一章(S ...

  7. Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  8. 【转】Android UI开发第二十四篇——Action Bar

    Action bar是一个标识应用程序和用户位置的窗口功能,并且给用户提供操作和导航模式.在大多数的情况下,当你需要突出展现用户行为或全局导航的activity中使用action bar,因为acti ...

  9. Python之路【第二十四篇】Python算法排序一

    什么是算法 1.什么是算法 算法(algorithm):就是定义良好的计算过程,他取一个或一组的值为输入,并产生出一个或一组值作为输出.简单来说算法就是一系列的计算步骤,用来将输入数据转化成输出结果. ...

随机推荐

  1. 885-螺旋矩阵 - III

    885-螺旋矩阵 - III 在 R 行 C 列的矩阵上,我们从 (r0, c0) 面朝东面开始 这里,网格的西北角位于第一行第一列,网格的东南角位于最后一行最后一列. 现在,我们以顺时针按螺旋状行走 ...

  2. Leetcode Week4 Find Minimum in Rotated Sorted Array II

    Question Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforeha ...

  3. AspxDashboardView 更新参数

    AspxDashboardView 更新参数 function SetThrendDashboardView() { console.log("就是这样被你征服"); var to ...

  4. WSO2 ESB XML定义语法(2)

    5.Proxy Service 配置 <proxy>元素用于定义Synapse代理服务. 通过基础Axis2引擎在指定的传输上创建和公开代理服务,根据标准的Axis2约定(即基于服务名称) ...

  5. 对Ajax返回的json数据做处理报错

    这个错误出现的原因是我再返回数据为json时,我页面的Ajax没有指定dataType: 'json'

  6. [Note]prufer

    [Note]Prufer编码 实现 不断删除度数为\(1\)的最小序号的点,并输出与其相连的节点的序号,直至树中只有两个节点. 性质 任何一棵\(n\)节点的树都可以唯一的用长度为\(n-2\)的pr ...

  7. JS全选按钮练习

    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/ ...

  8. Map.getOrDefault被坑小记与optional

    错误使用样例 map.getOrDefault("account","").toString(); 在运行几小时后,发现报错空指针:查看源码如下: defaul ...

  9. Jupyter Notebook快捷键总结

    1. Jupyter Notebook有两种mode Enter:进入edit模式 Esc:进入command模式 2. Command命令快捷键: A:在上方增加一个cell B:在下方增加一个ce ...

  10. 到头来还是逃不开Java - Java13核心类

    Java13核心类 没有特殊说明,我的所有学习笔记都是从廖老师那里摘抄过来的,侵删 引言 兜兜转转到了大四,学过了C,C++,C#,Java,Python,学一门丢一门,到了最后还是要把Java捡起来 ...