效率更高的循环队列-Java

数组队列中的出队,在时间复杂度上是O(n)复杂度,因为你删除数组首端,后面的数据都要左移一位。

我们可以记录一下队首的索引和队尾索引,入队时队尾索引+1,出队时队首索引-1,当队首索引与队尾索引相等时代表队列空了,完成一次循环。

入队五个元素,队尾索引(tail)自增五次

出队一个元素则队首索引(front)自增一次。

当数组尾部填满的时候不着急扩充,先看看数组首部是不是还有空余,有空余则指向数组首部。

此时才不能继续入队,若想要继续入队只能进行扩容,毕竟tail == front则队列会被视为空,我们有意识的浪费掉一个空间。

队尾索引+1对数组长度进行取余等于队首索引则队列满

(tail + 1) % capacity. length == front  

实现代码:

/**
 * 循环队列,不使用002章编写的动态数组,我们需要从底层写起
 */
public class LoopQueue<E> implements Queue<E> {
    private E[] data;
    private Integer tail, front;

    /**
     * 创建队列时指定队列最大容量
     *
     * @param capacity
     */
    public LoopQueue(Integer capacity) {
        // +1是因为循环数组会浪费一个空间,不明白可以看007号文档
        data = (E[]) new Object[capacity + 1];
        tail = 0;
        front = 0;
    }

    /**
     * 默认最大容量为10
     */
    public LoopQueue() {
        this(10);
    }

    @Override
    public void enqueue(E e) {
        //队列满了就去扩容为当前最大容量的两倍
        if ((tail + 1) % data.length == front)
            this.reCapacity((this.getCapacity() * 2));
        data[tail] = e;
        tail = (tail + 1) % data.length;//保证不大于等于数组总容量
    }

    @Override
    public E dequeue() {
        E buffer = null;
        if (tail != front) {
            buffer = data[front];
            data[front] = null;//删除掉且队首索引自增
            front = (front + 1) % data.length;//保证大于等于数组总容量
            //队列只占用总容量的四分之,则缩小为当前最大容量的一半
            if (this.getSize() <= this.getCapacity() / 4 && this.getCapacity() / 2 != 0)
                this.reCapacity(this.getCapacity() / 2);
        }
        return buffer;
    }

    @Override
    public E getFront() {
        return data[front];
    }

    @Override
    public Integer getSize() {
        if (tail > front)
            //当队尾索引大于队首索引时:队列size = tail - front
            return tail - front;
        else if (tail < front)
            //当队尾索引不为0且小于队首索引时:队列size = data.length - tail
            return data.length - tail;
        else if (tail == 0 && front != 0)
            //当队尾索引等于0且队首不为0时:队列size = data.length - front
            return data.length - front;
        return 0;//当队尾索引等于队首索引时:队列size = =0
    }

    @Override
    public Boolean isEmpty() {
        return this.getSize() == 0;
    }

    /**
     * 获取队列的最大容量
     *
     * @return
     */
    public Integer getCapacity() {
        return data.length - 1;//因为创建的时候+!了,所以获取的时候要-1
    }

    /**
     * 重新设置队列的最大容量
     *
     * @param newCapacity
     */
    private void reCapacity(Integer newCapacity) {
        E[] newData = (E[]) new Object[newCapacity + 1];
        Integer size = this.getSize();
        for (int i = 0; i < size; i++)
            // 因为是循环数组有一定的偏移,队首索引可能会大于队尾索引,所以要对原数组容量进行取余
            newData[i] = data[(i + front) % data.length];
        data = newData;
        front = 0;
        tail = size;
    }

    public String toString() {
        String buffer = "";
        Integer size = this.getSize();
        for (int i = 0; i < size; i++) {
            // 因为是循环数组有一定的偏移,队首索引可能会大于队尾索引,所以要对原数组容量进行取余
            buffer += data[(i + front) % data.length].toString();
            if (i != size - 1)
                buffer += ", ";
        }
        // 最左侧是队列首部
        return "Size:" + size + "\tLoopQueue[" + buffer + "]";
    }
}

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值