《玩转数据结构 从入门到进阶》 循环数组实现队列

《数据结构与算法》

本文来源于liuyubobobo的“玩转数据结构 从入门到进阶”视频教程

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

假设我们使用数组实现队列,请看下图

入队操作很快速,只需要在队尾添加元素即可。

但是,出队操作就耗时了,把队头元素删除并返回给调用者后,还需要把数组中的所有元素往前移动,这一步非常耗时。

a出队后,b、c、d要移动到下标为0、1、2的位置

有办法做到出队时,数组元素不移动吗?有滴,使用循环数组

循环队列有几个特点:

1、front变量指向队头(有元素),tail变量指向队尾(无元素)。

2、出队时front往后移动一个下标(“往后”这种说法不是完全正确,后面再给出精确定义;扩容后面会讲)

3、入队则是在队尾添加元素,队尾往后移动一个下标

4、front==tail表示队列为空。

队列满的时候,不是front、tail都指向同一个元素,而是tail在front的前一个位置。

上图是队列满的情况,数组的长度是8,但队列只能储存7个元素,必须让一个元素为空,tail指向这个元素。

是想下,如果非要用完数组的所有存储空间,此时front==tail;而队列为空是也是front==tail,在代码实现上要区分何时队列为空、何时队列满就复杂了。干脆就“浪费”一个元素内存,front==tail就只有队列为空这一种情况。

5、定义数组长度data.length(图中数组长度是8),队列满的时候判断条件是  (tail+1) % data.length == front ,大家照着图好好理解这个判断条件。

6、入队,tail移动方式是 tail = (tail + 1) % data.length

     出队,front移动方式是 front = (front + 1) % data.length

用上图举个例子

入队操作,tail = (1+1) % 8 = 2,tail变成2

出队操作,front = (3+1) % 8 = 4,front变成4

当front在7位置,出队,fornt = (7+1) % 8 = 0,此时front指向下标0的位置

7、扩容,如下图。满足条件(tail + 1) % data.length == front,此时需要扩容

实现代码如下

// 基于循环数组实现队列
public class LoopQueue<E>{
    private E[] data;  // 数组
    private int front, tail;  //队头、队尾
    private int size;  //队列元素大小

    public LoopQueue(){
        data = (E[]) new Object[11];  //初始化队列的数组
        // 初始化时队头、队尾下标都为0,队列大小也是0
        front = 0;
        tail = 0;
        size = 0;
    }

    // 队列容量是数组长度减1,因为要空一个元素,以便区别队列是否为空、队列是否满了需要扩容
    public int getCapacity(){
        return data.length -1;
    }

    //队列为空
    public boolean isEmpty(){
        return front == tail;
    }

    //队列大小
    public int getSize(){
        return size;
    }

    //扩容、缩容
    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];
        }
        front = 0;  //添加完成,队头在新数组下标为0的位置
        tail = size;  //添加完成,队列大小就是队尾的下标
        data = newData;  //队列的数组data指向扩容后的数组
    }

    //入队
    public void enqueue(E e){
        //如果队列满了,则扩容
        if ((tail + 1) % data.length == front){
            resize(getCapacity() * 2);
        }
        data[tail] = e;
        //队尾下标移动
        tail = (tail + 1) % data.length;
        //队列大小加一
        size++;
    }

    //出队
    public E dequeue(){
        if (isEmpty())
            throw new RuntimeException("没数据了");
        //队头数据存储到变量中
        E ret = data[front];
        data[front] = null;
        //队头下标移动
        front = (front + 1) % data.length;
        //队列大小减一
        size--;

        //这是缩容,如果队列大小小于容量的1/4且容量的1/2不为0,队列缩容为原来的1/2
        if (size < getCapacity()/4 && getCapacity()/2 != 0){
            resize(getCapacity()/2);
        }
        //返回队头数据
        return ret;
    }



    @Override
    public String toString(){

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

    public static void main(String[] args){

        _2_LoopQueue.LoopQueue<Integer> queue = new _2_LoopQueue.LoopQueue<>();
        for(int i = 0 ; i < 20 ; i ++){
            queue.enqueue(i);
            System.out.println(queue);

            if(i % 10 == 0){
                queue.dequeue();
                System.out.println(queue);
            }
        }
        for(int i = 0 ; i < 18 ; i ++){
            queue.dequeue();
            System.out.println(queue);
        }
    }

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值