【数据结构】队列

什么是队列?

队列是一种线性数据结构,要理解它,其实非常简单,举个例子。

假如高速公路上有一条隧道,所有通过隧道的车辆只允许从隧道的入口驶入,从隧道出口驶出,不允许逆行。因此,要想让车辆驶出隧道,只能按照车辆的驶入顺序,先驶入的车辆先驶出,后驶入的车辆后驶出,任何车辆都无法跳过它前面的车辆提前驶出。

队列是一种线性数据结构,它不同于栈,队列中的元素只能先进先出(First In First Out,简称FIFO),队列的出口端叫队首,队列的入口段叫队尾。

【数据结构】队列

队列的实现

1、入队(enqueue)

入队就是把新的元素放入队列中,只允许从队尾放入元素,并且新元素将会成为下一个新的队尾。

2、出队(dequeue)

出队就是把队首元素移除队列,出队元素的后一个元素成为新队首。

整体代码如下:

/**
 * 描述:队列所需的方法。
 *
 * Create By ZhangBiao
 * 2020/5/10
 */
public interface Queue<E> {

    /**
     * 入队操作
     *
     * @param e
     */
    void enqueue(E e);

    /**
     * 出队操作
     *
     * @return
     */
    E dequeue();

    /**
     * 获取队首
     *
     * @return
     */
    E getFront();

    /**
     * 获取队列元素个数
     *
     * @return
     */
    int getSize();

    /**
     * 判断队列是否为空
     *
     * @return
     */
    boolean isEmpty();

}
/**
 * 描述:基于动态数组实现队列。
 * <p>
 * Create By ZhangBiao
 * 2020/5/10
 */
public class ArrayQueue<E> implements Queue<E> {

    private Array<E> array;

    public ArrayQueue() {
        array = new Array<>();
    }

    public ArrayQueue(int capacity) {
        array = new Array<>(capacity);
    }

    @Override
    public void enqueue(E e) {
        this.array.addLast(e);
    }

    @Override
    public E dequeue() {
        return this.array.removeFirst();
    }

    @Override
    public E getFront() {
        return this.array.getFirst();
    }

    @Override
    public int getSize() {
        return this.array.getSize();
    }

    @Override
    public boolean isEmpty() {
        return this.array.isEmpty();
    }

    /**
     * 获取队列容量大小。
     *
     * @return
     */
    public int getCapacity() {
        return this.array.getCapacity();
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        result.append("Queue: ");
        result.append("front [");
        for (int i = 0; i < array.getSize(); i++) {
            result.append(array.get(i));
            if (i != array.getSize() - 1) {
                result.append(", ");
            }
        }
        result.append("] tail");
        return result.toString();
    }
}

循环队列的实现

注意:如果队列不断的出队,那么左边的内存空间就会浪费,这时就可以使用循环队列来维持队列容量。

循环队列是什么呢?

假设我们有一个这样的队列。如下:

【数据结构】队列

这时执行一个入队操作,在数组不扩容的前提下,如何让新元素入队并确定新的队尾位置呢?可以利用已出队元素留下的空间,让队尾指针重新指向数组的首位。

【数据结构】队列

这样一来,整个队列的元素就循环起来了。在物理存储上,队尾的位置可以在队首之前,当再有元素入队时,将其放入数组的首位,队尾指针后移即可。

【数据结构】队列

一直到(tail + 1)% 数组长度 == front 时,代表队列已满。注意:队尾指针指向的位置永远空出一位,所以在循环队列中会浪费一个空间。

【数据结构】队列

这就是所谓的循环队列。

整体代码如下:

/**
 * 描述:循环队列。
 * <p>
 * Create By ZhangBiao
 * 2020/5/10
 */
public class LoopQueue<E> implements Queue<E> {

    private E[] data;

    private int front;

    private int tail;

    private int size;

    public LoopQueue() {
        this(10);
    }

    public LoopQueue(int capacity) {
        data = (E[]) new Object[capacity + 1];
        front = 0;
        tail = 0;
        size = 0;
    }


    @Override
    public void enqueue(E e) {
        if ((tail + 1) % data.length == front) {
            resize(getCapacity() * 2);
        }
        data[tail] = e;
        tail = (tail + 1) % data.length;
        size++;
    }

    /**
     * 扩容
     *
     * @param newCapactiy
     */
    private void resize(int newCapactiy) {
        E[] newData = (E[]) new Object[newCapactiy + 1];
        for (int i = 0; i < size; i++) {
            newData[i] = data[(front + i) % data.length];
        }
        data = newData;
        front = 0;
        tail = size;
    }

    @Override
    public E dequeue() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Cannot dequeue from an empty queue.");
        }
        E ret = data[front];
        data[front] = null;
        front = (front + 1) % data.length;
        size--;
        if (size == getCapacity() / 4 && getCapacity() / 2 != 0) {
            resize(getCapacity() / 2);
        }
        return ret;
    }

    @Override
    public E getFront() {
        if (isEmpty()) {
            throw new IllegalArgumentException("Queue is empty.");
        }
        return data[front];
    }

    @Override
    public int getSize() {
        return size;
    }

    @Override
    public boolean isEmpty() {
        return front == tail;
    }

    public int getCapacity() {
        return data.length - 1;
    }

    @Override
    public String toString() {
        StringBuilder result = new StringBuilder();
        result.append(String.format("Queue: size = %d , capacity = %d\n", size, getCapacity()));
        result.append("front [");
        for (int i = front; i != tail; i = (i + 1) % data.length) {
            result.append(data[i]);
            if ((i + 1) % data.length != tail) {
                result.append(", ");
            }
        }
        result.append("] tail");
        return result.toString();
    }

    public static void main(String[] args) {
        LoopQueue<Integer> queue = new LoopQueue<>();
        for (int i = 0; i < 10; i++) {
            queue.enqueue(i);
            System.out.println(queue);
            if (i % 3 == 2) {
                queue.dequeue();
                System.out.println(queue);
            }
        }
    }

}

相关推荐