动态数组----循环队列的实现

1. 引入循环队列的原因:普通队列的出队时间复杂度O(n),不是按平均算的,因为每次出队都是O(n)。因此我们为了降低出队的时间复杂度,我们可以定义一个循环队列来实现队列的操作。

2. 循环队列的特点:即当队尾或队头指针到达尾部时,如需后移可重新指向表头。如图:其中front表示队头指针,rear表示队尾指针。

注意:我们在定义循环队列时会预留出一个空的位置,是尾指针rear始终指向这个空的空间,这样就会避免队满和队空条件一样的问题。避免了二异性。

3. 循环队列的接口:其中只定义一些循环队列操作的方法。

public interface LoopQueue<E> extends Iterable<E>{
    //相队尾添加元素
    public void offer(E element);
    //删除队首元素
    public E poll();
    //查看队首元素
    public E element();
    //判断队列是否为空
    public boolean isEmpty();
    //清空队列中的所有元素
    public void clear();
    //获取队列中有效元素的个数
    public int size();
}

4. 循环队列接口的实现类:该循环队列的实现思想也是动态数组,但是由于操作元素的特殊性,并不能直接由ArrayList或ArrayQueue实现,所以我们要从头开始定义ArrayLoopQueue。

//实现循环队列
public class ArrayLoopQueue<E> implements LoopQueue<E> {

    /*
    循环队列 我们要留出一个空的空间 使尾指针始终指向这个空的空间 这样对判断队满和队空时条件就不会相同 避免了二异性
    元素存储就像时一个环一样
    当(rear + 1)%data.length == front时 表示队满
    当rear == front(rear和front同时指向空的那块空空间)时 表示队空
     */
    //存放数据的容器
    private E[] data;
    //队首指针 每次更新为:(front+1)%data.length
    private int front;
    //队尾指针 每次更新为:(rear+1)%data.length
    private int rear;
    //有效元素的个数
    private int size;
    //容器默认容量
    private static int DEFAULT_CAPACITY = 10;

    //初始化data front rear size
    public ArrayLoopQueue(){
        data = (E[]) new Object[DEFAULT_CAPACITY + 1];
        front = 0;
        rear = 0;
        size = 0;
    }

    //向队尾添加元素
    //当队首指针和队尾指针关系为: (rear + 1) % data.length == front时表示队满了
    @Override
    public void offer(E element) {
        if((rear + 1) % data.length == front){ //判断循环队列是否已满
            resize(data.length * 2 - 1); //扩容循环队列
        }
        data[rear] = element;
        rear = (rear + 1) % data.length;
        size++;
    }

    //删除队首元素
    @Override
    public E poll() {
        if(isEmpty()){ //先判空
            throw new IllegalArgumentException("queue is not null");
        }
        E ele = data[front]; //先获取队首元素
        front = (front + 1) % data.length; //再更新队首指针
        size--;
        //判断是否满足数组缩容条件 若满足就进行缩容
        if(size <= data.length - 1 && data.length - 1 > DEFAULT_CAPACITY){
            resize(data.length / 2 + 1);
        }
        return ele;
    }

    //对数组进行缩容 / 扩容操作
    private void resize(int newLength) {
        E[] newData = (E[]) new Object[newLength];
        int index = 0;
        for(int i = front; i != rear; i = (i + 1) % data.length){
            newData[index++] = data[i];
        }
        data = newData; //将新数组赋给原数组
        front = 0; //更新队首指针 为新数组的队首0
        rear = index; //更新队尾指针 为新数组的队尾index
    }

    //查看队首元素
    @Override
    public E element() {
        if(isEmpty()){ //先判空
            throw new IllegalArgumentException("queue is not null");
        }
        //获取队首元素
        E ele = data[front];
        return ele;
    }

    //判断队列是否为空
    @Override
    public boolean isEmpty() {
        return front == rear;
    }

    //清空队列中的所有元素
    @Override
    public void clear() {
        data = (E[]) new Object[DEFAULT_CAPACITY + 1];
        front = 0;
        rear = 0;
        size = 0;
    }

    //获取队列中有效元素的个数
    @Override
    public int size() {
        return size;
    }

    //判断两个循环队列是否相等
    @Override
    public boolean equals(Object o) {
        //先判空
        if(o == null){
            return false;
        }
        //判断是否是自己
        if(this == o){
            return true;
        }
        //怕判断是否是同一类型
        if(o instanceof ArrayLoopQueue){
            ArrayLoopQueue<E> other = (ArrayLoopQueue<E>) o;
            //判断有效元素的个数是否相等
            if(this.size != other.size){
                return false;
            }
            //如果有效元素格式相等 就比较内容是否相等
            int i = this.front; //表示当前循环队列的索引
            int j = other.front; //表示指定循环队列的索引
            while (i != rear){
                //判断当前循环队列的当前索引位置的值 与 指定循环队列的当前索引的值 是否相等
                if(!data[i].equals(other.data[j])){
                    return false;
                }
                i = (i + 1) % data.length; //更新当前循环队列的索引
                j = (j + 1) % other.data.length; //更新指定循环队列的索引
            }
            return true;
        }
        return false;
    }

    //规定循环队列输出的格式
    @Override
    public String toString() {
        StringBuilder str = new StringBuilder();
        str.append("ArrayLoopQueue: " + size + "/" + (data.length - 1) + "[");
        if(isEmpty()){
            str.append(']');
        }
        for(int i = front; i != rear; i = (i + 1) % data.length){
            str.append(data[i]);
            if((i + 1) % data.length == rear){
                str.append(']');
            }else{
                str.append(',');
                str.append(' ');
            }
        }
        return str.toString();
    }

    //获取当前这个数据结构/容器 的 迭代器
    //通过迭代器对象 更方便挨个取出每一个元素
    //同时 实现了Iterable 可以让当前的数据结构/容器 被foreach循环遍历
    @Override
    public Iterator<E> iterator() {
        return new ArrayLoopQueueIterator();
    }

    //循环队列的迭代器
    class ArrayLoopQueueIterator implements Iterator<E>{

        private int cur = front; //表示游标 从循环队列的队首指针开始

        @Override
        public boolean hasNext() {
            return cur != rear; //当cur不等于rear时 表示可以继续迭代 有下一个元素
        }

        @Override
        public E next() {
            E ele = data[cur]; //获取当前游标cur的元素
            cur = (cur + 1) % data.length; //更新游标的值
            return ele;
        }
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值