队列的基本概念以及java实现队列

队列

  • 队列的基本概念
  • 队列的抽象数据类型
  • 顺序队列
    • 构造函数
    • 入队
    • 出队
    • 查询front
    • 获取队列长度
  • 链式队列
    • 构造函数
    • 入队
    • 出队
    • 查询front
    • 获取队列长度
  • 循环列队
    • 构造函数
    • 入队
    • 出队
    • 查询front
    • 获取循环队列的大小
    • 判断队列是否为空
    • 判断队列是否满

代码传送门,欢迎star:https://github.com/mcrwayfun/java-data-structure

1. 队列的基本概念

队列是一种限定存取位置的线性表。它只允许在表的一端插入,在另一端删除。允许插入的一端叫做队尾,允许删除的一端叫做队头。

每次在队尾加入元素,因此入队的顺序为a1,a2,a3,…,an,最先入队的元素最先出队,所以队列具有的这种特性成为先进先出FIFO(First In First Out

2. 队列的抽象数据类型

队列是一种特殊的线性表,其包含了一般线性表的操作,抽象数据类型ADT可以概括为两种:基于数组的存储表示和基于链表的存储表示。基于数组实现的是栈称为顺序队列,基于链表实现的栈称为链式队列。基本方法可以概括为

public interface Queue<E> {
    // 判断队列是否为空
    boolean isEmpty();
    // 入队
    void push(E data);
    // 出队
    E pop();
    // 查询front
    E peek();
    // 清空队列
    void clear();
    // 返回当前队列中的元素个数
    int length();
}

3. 顺序队列

顺序栈基于数组来存储表示,申明一个一维数组来存储数据

private Object[] elementData;

定义两个整型变量front和rear,分别表示为队头和队尾;队头front保存着即将出队的元素的索引,而队尾rear保存着下一个即将入队元素的索引。DEFAULT_SIZE为初始化数组的默认长度,capacity为数组中元素的个数

// 队列默认长度
private int DEFAULT_SIZE = 10;
// 保存数组的长度。
private int capacity;
// 队列头元素位置
private int front = 0;
// 队列尾元素位置
private int rear = 0;

3.1 构造函数

提供一个无参构造函数来初始化数组,数组长度为DEFAULT_SIZE

/**
  * 初始化数组长度和数组
  */
public SequenceQueue() {
  this.capacity = DEFAULT_SIZE;
  elementData = new Object[capacity];
}

提供value作为队列第一个元素,initSize作为数组的初始化长度

    /**
     * 以指定长度的数组来创建队列
     *
     * @param value    指定顺序中的第一个元素
     * @param initSize 数组长度
     */
    public SequenceQueue(E value, int initSize) {
        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = value;
        rear++;
    }

3.2 入队

检查队列是否已经满了,若不满则在队尾入队,且rear++

    /**
     * 入队
     *
     * @param data
     * @throws IndexOutOfBoundsException
     */
    @Override
    public void push(E data) {

        // 检查队列是否已经满了
        checkQueueIsFull();

        elementData[rear++] = data;
    }

3.3 出队

检查队列是否为空,若不为空则在队头出队,且front++

    /**
     * 出队,推出元素
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    @SuppressWarnings("unchecked")
    public E pop() {

        // 检查队列是否为空
        checkQueueIsEmpty();

        E oldValue = (E) elementData[front];
        // 释放队列已经出栈的元素
        elementData[front++] = null;
        return oldValue;
    }

3.4 查询front

    /**
     * 推出元素但不出队
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    @SuppressWarnings("unchecked")
    public E peek() {

        // 检查队列是否为空
        checkQueueIsEmpty();

        return (E) elementData[front];
    }

3.5 获取队列长度

    /**
     * 获取顺序队列的大小
     *
     * @return
     */
    @Override
    public int length() {
        return rear - front;
    }

4. 链式队列

链式队列使用结点的方式来存储表示数据,不用预先分配存储空间。使用front指针表示队头,rear指针表示队尾,size来统计当前队列中的结点数

    /**
     * 定义一个内部类Node,实现链表队列的结点
     *
     * @param 
     */
    private static class Node<E> {

        E item;
        Node next;

        public Node() {
        }

        public Node(E item, Node next) {
            this.item = item;
            this.next = next;
        }
    }

    /**
     * 队列头指针
     */
    private Node front;
    /**
     * 队列尾指针
     */
    private Node rear;
    /**
     * 队列包含的结点数
     */
    private int size;

4.1 构造函数

无参构造函数

    /**
     * 无参构造函数
     */
    public LinkQueue() {
        front = null;
        rear = null;
    }

以特定的数据来构造链表队列的头结点

    /**
     * 以特定的数据来构造链表队列的头结点
     *
     * @param data
     */
    public LinkQueue(E data) {

        front = new Node<>(data, null);
        rear = front;
        size++;
    }

4.2 入队

分两种情况,队列为空和不为空。若队列为空,则队头指针和队尾指针均指向新结点;若不为空,则队尾指针rear.next = newNode,且newNode成为新的队尾

    /**
     * 入队
     *
     * @param data
     */
    @Override
    public void push(E data) {
        Node newNode = new Node<>(data, null);
        // 当前队列为空
        if (isEmpty()) {
            front = newNode;
            rear = newNode;
        } else {
            // 队列存在元素
            // 尾结点的next指向新结点
            rear.next = newNode;
            // 新结点成为新的尾结点
            rear = newNode;
        }
        size++;
    }

4.3 出队

先检查队列是否为空,若不为空才能执行出队操作。出队元素为当前front指针指向的结点

    /**
     * 出队,并删除头元素
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    public E pop() {

        // 检测队列是否空
        checkQueueIsEmpty();
        Node oldFront = front;
        front = front.next;
        oldFront.next = null;
        size--;
        return oldFront.item;
    }

4.4 查询front

当前front指向的结点即为队头

    /**
     * 查找队列的front元素
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    public E peek() {

        // 检测队列是否空
        checkQueueIsEmpty();
        return front.item;
    }

4.5 获取队列长度

    /**
     * 返回队列长度
     *
     * @return
     */
    @Override
    public int length() {
        return size;
    }

5. 循环列队

这是一个顺序队列,由图中可以得知,当front == rear的时候,队列为空。当rear = maxSize(maxSize = 数组长度),队列满,如果再加入新的元素,则会产生溢出。但是,这种溢出是一种假溢出,因为数组的前端可能还存在空位置,为了能够充分利用数组中的元素,把数组的前端和后端连接起来,形成一个环形的表,这就是循环队列。

循环队列首尾相接,当队头指针front和队尾rear进到maxSize-1时,在前进一个位置就自动到0,所以:
**队头指针进1:front = (front + 1)% maxSize
队尾指针进1:rear = (rear + 1) % maxSize**

队列为空的判断条件为front == rear,为了区别,队满的判断条件为front == (rear + 1) % maxSize,即当rear指向front的前一位置时就认为队列已经满了。还有一种判断队列满的方法,定义一个flag,当出队时,让flag = 0;入队时,让flag = 1。当遇到front == rear,若flag = 0,则队列空;若flag = 1,则队列满

    // 队列默认长度
    private int DEFAULT_SIZE = 10;

    // 用来保存队列元素的数组
    private Object[] elementData;
    // 保存数组的长度。
    private int capacity;
    // 队列头元素位置
    private int front = 0;
    // 队列尾元素位置
    private int rear = 0;

上述代码是操作循环队列申明的变量,用数组elementData来存储数据,capacity标识数组的长度。因为存在数组,我们可以利用elementData[rear]elementData[front]来替换flag。当遇到front == rear时,若elementData[rear] == null,则队列空;若elementData[front] != null,则队列满

5.1 构造函数

无参构造函数,使用默认长度DEFAULT_SIZE来初始化数组长度

    public LoopQueue() {
        this.capacity = DEFAULT_SIZE;
        elementData = new Object[capacity];
    }

以初始元素和初始长度来构建循环队列

    /**
     * 以初始元素和初始长度来构建循环队列
     *
     * @param data
     * @param initSize
     */
    public LoopQueue(E data, int initSize) {

        this.capacity = initSize;
        elementData = new Object[capacity];
        elementData[0] = data;
        rear++;
    }

5.2 入队

入队前先判断队是否满了,若满了则不能入队。将data放入数组,且rear++。判断rear == capacity,若等于则从下标0开始

     /**
     * 入队
     *
     * @param data
     * @throws IndexOutOfBoundsException
     */
    @Override
    public void push(E data) {

        // 判断队列是否已经满了
        checkQueueIsFull();
        elementData[rear++] = data;
        // rear到头则rear转头
        rear = rear == capacity ? 0 : rear;
    }

5.3 出队

出队前先判断队列是否为空,若空了则不能出队。出队后front++,判断front == capacity,若等于则从下标0开始

    /**
     * 出队并删除头元素
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    @SuppressWarnings("unchecked")
    public E pop() {

        // 判断队列是否为空
        checkQueueIsEmpty();
        E oldValue = (E) elementData[front];
        elementData[front++] = null;
        // front到头则front转头
        front = front == capacity ? 0 : front;
        return oldValue;
    }

5.4 查询front

    /**
     * 查找队列的第一个元素
     *
     * @return
     * @throws IndexOutOfBoundsException
     */
    @Override
    @SuppressWarnings("unchecked")
    public E peek() {

        // 判断队列是否为空
        checkQueueIsEmpty();
        return (E) elementData[front];
    }

5.5 获取循环队列的大小

分两种情况,如果front < rear,有效元素就是front到rear之间的元素,直接用rear - front即可;如果front >= rear,有效元素为front->capacity之间,0->front之间的

    /**
     * 获取循环队列的大小
     *
     * @return
     */
    @Override
    public int length() {

        if (isEmpty()) {
            return 0;
        } else {
            return rear - front > 0 ? rear - front
                    : capacity - (front - rear);
        }
    }

5.6 判断队列是否为空

    /**
     * 判断队列是否为空
     *
     * @return
     */
    @Override
    public boolean isEmpty() {
        return rear == front
                && elementData[rear] == null;
    }

5.7 判断队列是否满

    /**
     * 判断队列是否已经满了,若满了则抛出IndexOutOfBoundsException
     */
    private void checkQueueIsFull() {

        if (rear == front && elementData[front] != null) {
            // throw exception
        }
    }

你可能感兴趣的:(数据结构)