Java-数据结构(队列)

116 阅读5分钟

队列(Queue)

1.1 队列的简介

队列是一种特殊的线性表,特殊之处在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作,队列是一种操作受限制的线性表。进行插入操作(入口)的端称为队尾,进行删除操作(出口)的端称为队头。

image.png

队列的插入操作只能在队尾操作,队列的删除操作只能在队头操作,因此队列是一种先进先出(First In First Out)的线性表,简称FIFO表。

  • 顺序队列图解: image.png

  • 环形队列图解:

image.png

LinkedList类实现了Queue接口,在Queue接口中提供的方法可以实现队列的相关操作。

队列的体系结构:

image.png

Queue接口扩展了Collection接口,常见的方法如下所示。

方法名描述
boolean add(E e);这两个方法都是在尾部添加元素add()会在长度不够时抛出异常; offer()则不会,只返回false
boolean offer(E e);
E element();这两个方法都是查看头部元素 ,返回头部元素,但不改变队列element()会在没元素时抛出异常; peek()则返回null;
E peek();
E remove();这两个方法都是删除头部元素,返回头部元素,并且从队列中删除remove()会在没元素时抛出异常; poll()返回null;
E poll();
boolean isEmpty();判断队列是否为空,如果为空则返回true;否则返回false。

【示例】使用LinkedList实现队列操作

package com.huayu.queue;
import java.util.LinkedList;
import java.util.Queue;
/*
    @Author xiangge
    @Date 2023/7/17 
    @Description 队列的介绍
         队列在JDK中的形式是:Queue接口,也就是说实现了Queue接口的类都具有队列的功能
​
        队列的体系结构:
            LinkedList -实现--> Deque  -继承--> Queue -继承--> Collection
        Queue接口的常用方法:
        boolean add(E e); 在队列尾部添加元素, add()会在长度不够时抛出异常
        boolean offer(E e); 在队列尾部添加元素, offer()则不会,只返回false
        E element();   查看头部元素 ,返回头部元素,但不改变队列 , element()会在没元素时抛出异常;
        E peek();      查看头部元素 ,返回头部元素,但不改变队列 ,  peek()则返回null;
        E remove();  删除头部元素,返回头部元素,并且从队列中删除,remove()会在没元素时抛出异常;
        E poll();    删除头部元素,返回头部元素,并且从队列中删除,poll()返回null;
        boolean isEmpty(); 判断队列是否为空,如果为空则返回true;否则返回false。
*/
public class QueueDemo1 {
    public static void main(String[] args) {
        Queue<Integer> queue = new LinkedList<>();
        queue.add(2);
        queue.add(5);
        queue.add(8);
        queue.add(1);
        queue.add(4);
        System.out.println("queue = " + queue); // list = [2, 5, 8, 1, 4]
​
        // 遍历队列并删除队列元素
        while(!queue.isEmpty()){
            Integer remove = queue.remove();
            System.out.println("remove = " + remove);
        }
        System.out.println("queue = " + queue);
    }
}

1.2 队列的实现

1.2.1 数组模拟队列实现

【示例】顺序队列的实现

public class ArrayQueue<T> {
    /**
     * 模拟队列的数组
     */
    private Object[] elementData;
    /**
     * 保存队首的指针
     */
    private int front;
    /**
     * 保存队尾的指针
     */
    private int rear;
    /**
     * 无参构造方法
     */
    public ArrayQueue() {
        // 设置数组的默认空间长度为10
        this(10);
    }
​
    /**
     * 有参构造方法
     * @param capcaity 设置数组的空间长度
     */
    public ArrayQueue(int capcaity) {
        // 处理capcaity参数不合法的情况
        if (capcaity < 0) {
            throw new IllegalArgumentException("参数不合法异常,capcaity:" + capcaity);
        }
        // 创建指定空间长度的数组
        this.elementData = new Object[capcaity];
    }
​
    /**
     * 获取队列中元素的个数
     * @return 返回队列中元素的个数
     */
    public int getSize() {
        return rear - front;
    }
​
    /**
     * 入队列操作
     * @param element
     */
    public void add(T element) {
        // 判断队列是否已满
        if (isFull()) {
            throw new RuntimeException("队列已满,无法执行入队列操作");
        }
        // 执行入队操作
        elementData[rear++] = element;
    }
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列首元素
        T element = (T)elementData[front];
        // 把front索引位置元素设置为默认值,并更新front索引值
        elementData[front++] = null;
        // 返回被删除的队首元素
        return element;
    }
​
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 获取并返回队列的首元素
        return (T)elementData[front];
    }
​
    /**
     * 判断队列是否已满
     * @return 队列已满,则返回true;队列未满,则返回false。
     */
    private boolean isFull() {
        return rear == elementData.length;
    }
​
    /**
     * 判断队列是否为空
     * @return 队列为空,则返回true;队列不为空,则返回false。
     */
    public boolean isEmpty() {
        return front == rear;
    }
}

【示例】循环队列的实现

public class ArrayQueue<T> {
    /**
     * 模拟队列的数组
     */
    private Object[] elementData;
    /**
     * 保存队首的指针
     */
    private int front;
    /**
     * 保存队尾的指针
     */
    private int rear;
    /**
     * 无参构造方法
     */
    public ArrayQueue() {
        // 设置数组的默认空间长度为10
        this(10);
    }
​
    /**
     * 有参构造方法
     * @param capcaity 设置数组的空间长度
     */
    public ArrayQueue(int capcaity) {
        // 处理capcaity参数不合法的情况
        if (capcaity < 0) {
            throw new IllegalArgumentException("参数不合法异常,capcaity:" + capcaity);
        }
        // 创建指定空间长度的数组
        this.elementData = new Object[capcaity];
    }
​
    /**
     * 获取队列中元素的个数
     * @return 返回队列中元素的个数
     */
    public int getSize() {
        if (rear >= front) {
            return rear - front;
        }
        else {
            return (rear + elementData.length) - front;
        }
    }
​
    /**
     * 入栈操作
     * @param element
     */
    public void add(T element) {
        // 判断队列是否已满
        if (isFull()) {
            throw new RuntimeException("队列已满,无法执行入队列操作");
        }
        // 执行入队操作
        elementData[rear] = element;
        // 更新rear的值
        rear = (rear + 1) % elementData.length;
    }
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列首元素
        T element = (T)elementData[front];
        // 把front索引位置元素设置为默认值
        elementData[front] = null;
        // 更新front索引值
        front = (front + 1) % elementData.length;
        // 返回被删除的队首元素
        return element;
    }
​
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 获取并返回队列的首元素
        return (T)elementData[front];
    }
​
    /**
     * 判断队列是否已满
     * @return 队列已满,则返回true;队列未满,则返回false。
     */
    private boolean isFull() {
        return front == (rear + 1) % elementData.length;
    }
​
    /**
     * 判断队列是否为空
     * @return 队列为空,则返回true;队列不为空,则返回false。
     */
    public boolean isEmpty() {
        return front == rear;
    }
}

1.2.2 链表模拟队列实现

public class LinkedListQueue<T> {
    /**
     * 保存队列首节点
     */
    private QueueNode<T> front;
    /**
     * 保存队列尾结点
     */
    private QueueNode<T> rear;
    /**
     * 保存实际存放元素的个数
     */
    private int size;
    /**
     * 获取队列实际存储元素的个数
     */
    private int getSize() {
        return size;
    }
​
    /**
     * 入队列操作
     * @param element
     */
    public void add(T element) {
        // 把需要入队的元素封装成节点对象
        // 处理链表为空的情况
        if (rear == null) {
            // 把元素封装成对象,并且只为队尾
            rear = new QueueNode<>(element, null);
            // 更新front的值
            front = rear;
        }
        // 处理链表非空的情况
        else {
            // 把元素封装成对象,并添加到队尾
            rear.next = new QueueNode<>(element, null);
            // 更新rear的值
            rear = rear.next;
        }
        // 更新size的值
        size++;
    }
​
​
    /**
     * 删除队列的首元素
     * @return 返回被删除的元素值
     */
    public T remove() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执删除队列操作");
        }
        // 获取队列的首元素值
        T element = front.data;
        // 更新front的指向,让front指向其下一个节点
        front = front.next;
        // 更新size的值
        size--;
        // 返回出队的元素
        return element;
    }
    
    /**
     * 获得队列的首元素
     * @return 返回队列的首元素
     */
    public T element() {
        // 判断队列是否为空
        if (isEmpty()) {
            throw new RuntimeException("队列为空,无法执获取队列操作");
        }
        // 返回队列首元素
        return front.data;
    }
​
    /**
     * 判断队列是否为空
     * @return 如果队列为空,则返回true;如果队列非空,则返回false。
     */
    public boolean isEmpty() {
        return size == 0;
    }
​
    /**
     * 单链表节点类
     */
    private static class QueueNode<T> {
        /**
         * 节点中存放的数据
         */
        private T data;
        /**
         * 指向下一个节点
         */
        private QueueNode<T> next;
        /**
         * 构造方法
         */
        public QueueNode(T data, QueueNode<T> next) {
            this.data = data;
            this.next = next;
        }
    }
}