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. 如何处理pom文件中没有找到HUB检查到高危漏洞的依赖包

    最近使用HUB工具检查到maven工程中存在高危险漏洞,虽然定位到具体的引用包了,但是在pom文件中却没有发现该依赖包.此时,我们就需要用到这条命令mvn dependency:tree,该命令会将m ...

  2. LaTeX技巧008:如何给文字添加阴影效果?

    大家可以使用这个包:shadowtext宏包

  3. Yii2 JWT

    Yii2 JWT 这个扩展为Yii framework 2.0提供了JWT集成(需要PHP 5.6+).它包括基本的HTTP身份验证支持. 目录 安装 依赖关系 基本用法 创建 从字符串分析 验证 令 ...

  4. C++——标准模板库

    1.泛型程序设计:将程序尽可能写的通用,将算法从特定的数据结构中抽象出来,成为通用的:C++模板为泛型程序设计奠定了关键的基础:STL是泛型程序设计的一个范例:容器container.迭代器itera ...

  5. 在npm上如何发布自己的包

    环境需要:node和npm 然后在本地建一个文件夹作为你要发布的模块 然后在这个文件夹下打开cmd 执行:npm init 按照提示写对应的,或者先不写也可以,最后y 这时候在mcrm这个文件夹下就出 ...

  6. C语言回文链表

    要求:请判断一个链表是否为回文链表. 示例 1: 输入: 1->2 输出: false 示例 2: 输入: 1->2->2->1 输出: true思路:利用快慢双指针+反转半链 ...

  7. OpenCV3+VS2015 经常出现debug error abort()has been called问题

    方案1:图片路径错误:查看imread的路径

  8. 高效完成R代码

    为什么R有时候运行慢? 参考https://www.cnblogs.com/qiaoyihang/p/7779144.html 一.为什么R程序有时候会很慢? 1.计算性能的三个限制条件 cpu ra ...

  9. 巨杉Tech | SequoiaDB虚机镜像正式上线

    数据库云化架构需求 随着云架构的发展和流行,在业务和应用进行“云化”的过程中,云数据库因为在整体架构中的重要地位,在云化改造中的重要性不言而喻.云数据库需要满足这些技术要求,除了在功能上的具体提升,在 ...

  10. go语言 实现哈希算法

    验证结果网址 http://www.fileformat.info/tool/hash.htm "golang.org/x/crypto/md4"不存在时,解决方法: cd $GO ...