一文详解「队列」,手撸队列的3种方法!

简介: 一文详解「队列」,手撸队列的3种方法!

本文已收录至我的 Github《算法图解》系列:https://github.com/vipstone/algorithm

前面我们介绍了栈(Stack),队列和栈是比较像的一种数据结构。我们可以想象有很多辆汽车正在通过单行道的隧道,所有车辆不能插队、不能掉头,先进来的车也先出去,我们可以把这种特征的数据结构称之为队列。

image.png队列也属于逻辑结构,所谓的物理结构是指可以将数据存储在物理空间中,比如数组和链表都属于物理数据结构;而逻辑结构则是用于描述数据间的逻辑关系的,它可以由多种不同的物理结构来实现,比如队列和栈都属于逻辑结构。


队列特性

队列中的元素必须是先进先出(First In First Out,FIFO)的,它有两个重要的方法:入队(enqueue)和出队(dequeue)。队列的入口端叫队尾(rear),出口端叫队头(front),如下图所示:

image.png

手撸队列

学习了队列的基本知识之后,接下来我们将使用代码来实现一个队列。

首先我们先使用数组来实现一个队列,它的结构如下图所示:

image.png

1.自定义队列—数组

public class MyQueue<E> {
    private Object[] queue; // 存储容器
    private int head; // 头部指针
    private int tail; // 尾部指针
    private int size; // 队列实际存储长度
    private int maxSize; // 最大容量
    public MyQueue() {
        // 无参构造函数,设置默认参数
        this.maxSize = 10;
        this.head = 0;
        this.tail = -1;
        this.size = 0;
        this.queue = new Object[this.maxSize];
    }
    public MyQueue(int initSize) {
        // 有参构造函数,设置参数
        this.maxSize = initSize;
        this.head = 0;
        this.tail = -1;
        this.size = 0;
        this.queue = new Object[this.maxSize];
    }
    /**
     * 查询队头元素
     */
    public E peek() throws Exception {
        if (size == 0) {
            throw new Exception("队列中暂无数据");
        }
        return (E) this.queue[this.head];
    }
    /**
     * 入列
     */
    public boolean offer(E e) throws Exception {
        if (tail >= (maxSize - 1)) {
            throw new Exception("添加失败,队列已满");
        }
        this.queue[++tail] = e;
        size++;
        return true;
    }
    /**
     * 出列
     */
    public E poll() throws Exception {
        if (size == 0) {
            throw new Exception("删除失败,队列为空");
        }
        size--;
        return (E) this.queue[head++];
    }
    /**
     * 代码测试
     */
    public static void main(String[] args) throws Exception {
        MyQueue queue = new MyQueue();
        queue.offer("Hello");
        queue.offer("Java");
        System.out.println(queue.peek());
        queue.poll();
        System.out.println(queue.poll());
    }
}


以上代码的执行结果如下:

Hello

Java


2.自定义队列—链表

用链表实现队列的数据结构如下图所示:

image.png

实现代码如下:

public class QueueByLinked {
    /**
     * 声明链表节点
     */
    static class Node<E> {
        E item; // 当前的值
        Node<E> next; // 下一个节点
        Node(E e) {
            this.item = e;
        }
    }
    private Node firstNode; // 队头元素
    private Node lastNode; // 队尾元素
    private int size; // 队列实际存储数量
    private int maxSize; // 队列最大容量
    public QueueByLinked(int maxSize) {
        if (maxSize <= 0) throw new RuntimeException("队列最大容量不能为空");
        // 默认初始化函数
        firstNode = lastNode = new Node(null);
        this.size = 0;
        this.maxSize = maxSize;
    }
    /**
     * 判断队列是否为空
     */
    public boolean isEmpty() {
        return size == 0;
    }
    /**
     * 入列
     */
    public void offer(Object e) {
        // 最大值效验
        if (maxSize <= size) throw new RuntimeException("队列已满");
        Node node = new Node(e);
        lastNode = lastNode.next = node; // 设置最后一个节点和倒数第二个节点的 next
        size++; // 队列数量 +1
    }
    /**
     * 出列
     */
    public Node poll() {
        if (isEmpty()) throw new RuntimeException("队列为空");
        size--; // 队列数量 -1
        return firstNode = firstNode.next; // 设置并返回队头元素(第一个节点是 null,当前元素则为 Node.next)
    }
    /**
     * 查询队头元素
     */
    public Node peek() {
        if (isEmpty()) throw new RuntimeException("队列为空");
        return firstNode.next;  // 返回队头元素(第一个节点是 null,当前元素则为 Node.next)
    }
    /**
     * 代码测试
     */
    public static void main(String[] args) {
        QueueByLinked queue = new QueueByLinked(10);
        queue.offer("Hello");
        queue.offer("JDK");
        queue.offer("Java");
        System.out.println(queue.poll().item);
        System.out.println(queue.poll().item);
        System.out.println(queue.poll().item);
    }
}


以上代码的执行结果如下:

Hello

JDK

Java


3.扩展:使用 List 实现自定义队列


除了以上两种方式之外,我们还可以使用 Java 自身的数据结构来实现队列,比如 List,我们这里提供一个实现的思路(但并不建议在实际工作中使用),实现代码如下:

import java.util.ArrayList;
import java.util.List;
/**
 * 自定义队列(List方式)
 */
public class QueueByList<E> {
    private List value; // 队列存储容器
    public QueueByList() {
        // 初始化
        value = new ArrayList();
    }
    /**
     * 判断队列是否为空
     */
    public boolean isEmpty() {
        return value.size() == 0;
    }
    /**
     * 入列
     */
    public void offer(Object e) {
        value.add(e);
    }
    /**
     * 出列
     */
    public E poll() {
        if (isEmpty()) throw new RuntimeException("队列为空");
        E item = (E) value.get(0);
        value.remove(0);
        return item;
    }
    /**
     * 查询队头元素
     */
    public E peek() {
        if (isEmpty()) throw new RuntimeException("队列为空");
        return (E) value.get(0);
    }
    /**
     * 代码测试
     */
    public static void main(String[] args) {
        QueueByList queue = new QueueByList();
        queue.offer("Hello");
        queue.offer("JDK");
        queue.offer("Java");
        System.out.println(queue.poll());
        System.out.println(queue.poll());
        System.out.println(queue.poll());
    }
}


以上代码的执行结果如下:

Hello

JDK

Java


队列使用场景

队列的常见使用场景有:

  • 存储多线程中等待排队执行的任务;
  • 存储多线程公平锁中等待执行任务的线程;
  • 常见消息中间件的任务队列等。


总结

通过以上三种队列的实现方式我们可以看出,任意容器都是可以用来实现队列(Queue)的,只要保证队列的元素先进先出(FIFO),并且在实现类中需要包含队列的四个核心方法:入列、出列、查询队列是否为空、返回队头元素等,就可以称为实现了一个自定义的队列。

最后,给大家留一个问题:队列的类型都有哪些?欢迎评论区留言~


关注我,每天陪你进步一点点!

image.png

相关文章
|
18天前
|
消息中间件
手撸MQ消息队列——循环数组
队列是一种常用的数据结构,类似于栈,但采用先进先出(FIFO)的原则。生活中常见的排队场景就是队列的应用实例。在数据结构中,队列通常用数组实现,包括入队(队尾插入元素)和出队(队头移除元素)两种基本操作。本文介绍了如何用数组实现队列,包括定义数组长度、维护队头和队尾下标(front 和 tail),并通过取模运算解决下标越界问题。此外,还讨论了队列的空与满状态判断,以及并发和等待机制的实现。通过示例代码展示了队列的基本操作及优化方法,确保多线程环境下的正确性和高效性。
23 0
手撸MQ消息队列——循环数组
|
4月前
|
Java 程序员
惊呆了!LinkedList的这些队列功能,99%的程序员都没用过!
【6月更文挑战第18天】`LinkedList`不仅是Java集合中的列表实现,还可作队列(`peek()`,`add()`,`remove()`)和双端队列(`Deque`,`addFirst()`,`addLast()`,`peekFirst()`,`peekLast()`),甚至栈(`push()`,`pop()`,`peek()`)。常被低估,其实它具备从两端操作数据的强大能力,适合多种数据结构需求。
34 6
|
5月前
|
API iOS开发
总是搞不懂的同步异步,阻塞非阻塞
总是搞不懂的同步异步,阻塞非阻塞
60 0
|
5月前
|
安全
带你手搓阻塞队列——自定义实现
带你手搓阻塞队列——自定义实现
68 0
|
12月前
|
存储 算法 JavaScript
带你读《图解算法小抄》十八、队列(3)
带你读《图解算法小抄》十八、队列(3)
|
12月前
|
存储 算法 JavaScript
带你读《图解算法小抄》十八、队列(2)
带你读《图解算法小抄》十八、队列(2)
|
12月前
|
存储 缓存 算法
带你读《图解算法小抄》十八、队列(1)
带你读《图解算法小抄》十八、队列(1)
|
Java
java数据结构31:银行业务队列简单模拟
设某银行有A、B两个业务窗口,且处理业务的速度不一样,其中A窗口处理速度是B窗口的2倍---即当A窗口处理完2个顾客时,B窗口处理完一个顾客。给定到达银行的顾客序列,请按业务完成的顺序输出顾客序列。假定不考虑顾客先后到达的时间间隔,并且当不同窗口同时处理完2个顾客时,A窗口顾客优先输出。
179 0
|
前端开发
前端知识案例-队列
前端知识案例-队列
50 0
前端知识案例-队列
|
消息中间件 存储 前端开发
面试官让我手写队列,差点没写出来,回来后赶忙把重点记下来
栈和队列是一对好兄弟,前面我们介绍过一篇栈的文章(栈,不就后进先出),栈的机制相对简单,后入先出,就像进入一个狭小的山洞,山洞只有一个出入口,只能后进先出(在外面的先出去,堵在里面先进去的就有点倒霉)。而队列就好比是一个隧道,后面的人跟着前面走,前面人先出去(先入先出)。日常的排队就是队列运转形式的一个描述!
106 0
面试官让我手写队列,差点没写出来,回来后赶忙把重点记下来