队列

队列(Queue)是一种线性数据结构。相关操作也比数组简单的多,但是队列与栈不同的是,队列只能从一段(队尾)添加元素,从另一端(队首)取出元素,即先进先出(FIFO)。

实现自己的队列

首先定义一个顶层接口:

/**
 * @author Dongguabai
 * @date 2018/9/13 9:16
 */
public interface Queue<E> {

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

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

    /**
     * 查看队首的元素
     * @return
     */
    E getFront();

    /**
     * 元素个数
     * @return
     */
    int getSize();

    /**
     * 是否为空
     * @return
     */
    boolean isEmpty();
}

基于动态数组实现的队列(关于动态数组可参看:https://blog.csdn.net/Dongguabai/article/details/82189286):

/**
 * @author Dongguabai
 * @date 2018/9/13 9:20
 */
@ToString
public class ArrayQueue<E> implements Queue<E>{

    private MyArrayList<E> array;

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

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

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

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

    public int getCapacity(){
        return array.getCapacity();
    }

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

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

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

}

从上面的代码也可以看出一个问题,在执行dequeue()方法时候,在队列的元素数量很大的时候是特别影响性能的,因为删除队首的元素,后面所有的元素都要向前挪一位,即时间复杂度为O(n)。

当a元素出队后,后面的b、c、d、e元素需要向前挪一位,同时size--。

解决办法就是我们可以使用front记录队首的元素位置,使用tail记录下次插入的元素的位置,这样每次出队列的时候,我们只需要维护front即可,即front++。

于是就出现了循环队列。

当队列为空的时候,front指向0,tail也指向0,即队列为空的时候front==tail:

这时候新增一个元素a,front不变,tail++,以此类推:

如果发生了出队操作,只需要fron++,tail不变:

以此类推,那为什么叫循环队列呢,因为如果此时队列元素情况如下:

如果再新增一个元素f,此时tail就不能++了,而此时队列前面还有空着的空间,这就可以成为循环队列,即数组可以看成一个环了。也就是说tail不能简单的执行++操作,应该是(tail+1)%capacity,在这里就是0,即此时tail应该指向索引为0的位置:

这样的话,再有新的元素进来了就将进入索引为0 的位置,tail继续加一:

但是这样问题就来了,之前说过当front==tail即表示队列为空,如果新的元素进入索引为1的位置,那么tail++,而此时front==tail,这样当front==tail即表示队列为空这个条件就不成立了,因为此时队列已满,这个不是我们所希望看到的。所以可以定义tail+1==front即表示队列中的数组已满,准确来说应该是(tail+1)%capacity==front,此时可以进行扩容处理,这样的话也就是说我们“有意识的浪费了一个空间”。

接下来实现自己的循环队列:

/**
 * 循环队列
 *
 * @author Dongguabai
 * @date 2018/9/13 10:27
 */
@ToString
public class LoopQueue<E> implements Queue<E> {

    private E[] data;
    private int front;
    private int tail;

    //也可以不需要size
    private int size;


    public LoopQueue(int capacity) {
        //需要浪费一个空间
        data = (E[]) new Object[capacity + 1];
        front = 0;
        tail = 0;
        size = 0;
    }

    public LoopQueue() {
        this(10);
    }

    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);
            }
        }
    }

    public int getCapacity() {
        //需要浪费一个空间
        return data.length - 1;
    }

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

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

    @Override
    public void enqueue(E e) {

        if ((tail + 1) % data.length == front) {
            //浪费一个空间,即扩容就是实际容量*2
            resize(getCapacity() * 2);
        }
        data[tail] = e;
        tail = (tail + 1) % data.length;
        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];
    }

    private void resize(int newCapacity) {

        E[] newData = (E[]) new Object[newCapacity + 1];
        for (int i = 0; i < size; i++) {
            newData[i] = data[(i + front) % data.length];
        }
        data = newData;
        front = 0;
        tail = size;
    }

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值