数组实现队列(java)


如何用数组优雅且正确地实现一个队列呢

**

本文采用我思考时的思路,分别展示了两版代码,第一版存在问题,第二版基本无误(如果有错欢迎指正)

第一版

public class ArrayQueue<AnyType> {

    private AnyType[] dataElem;    //存放元素的基础数组
    private int size;              //记录下元素的个数
    private static final int DEFAULT_CAPACITY = 10;   //数组默认容量
    private int front;            //永远指向队首元素(下标)
    private int rear;             //永远指向队尾元素(下标)

    public ArrayQueue(){

        ensureCapacity(DEFAULT_CAPACITY);   //利用扩容函数初始化基础数组
        size = 0; 
        //因为进队操作rear要加一,所以rear要初始化为-1, 
        //当第一个元素进队,rear刚好指向第一个元素的下标0,以后每次进队加一  
        rear = -1;  
        //因为第一次进队元素被放在下标为0的位置,所以front默认初始化为0,
        //刚好是第一个元素下标  
        front = 0;

    }
    //扩容操作
    private void ensureCapacity(int newCapacity) {
        if(newCapacity <= size)    //必须要保证新容量要比当前元素个数要大,
            return;                //否则扩容没有什么意义
        AnyType[] oldDataElem = dataElem;
        //不能创建泛型数组,所以先创建Object类型数组,然后强转
        dataElem = (AnyType[]) new Object[newCapacity]; 
        for(int i = 0; i < size(); i++)
            dataElem[i] = oldDataElem[i]}
    //返回当前队列储存元素的个数
    public int size() {
        return size;
    }

    //入队,每次入队要将rear、size加一
    public void enqueue(AnyType elem){
        if(size() == dataElem.length)   //容量不够,扩容
            ensureCapacity(size() * 2);
        //每当rear要越过dataElem.length - 1时,
        //就要让rear归为0(因为此时数组首端可能还有空位,前面经过了出队操作,
        //导致了front前移,而且size还没有等于0),
        //否则会出现ArrayIndexOutOfBoundsException
        if(++rear == dataElem.length)
            rear = 0;
        dataElem[rear] = elem;
        size++;
    }

    //出队,每次出队要将front加一,size减一
    public AnyType dequeue(){
        if(size() == 0)
            throw new NoSuchElementException();
        AnyType oldValue = dataElem[front++];
        //因为这是一个循环队列,每当front要越过dataElem.length - 1时,
        //就要让front归为0(因为此时数组首端还有元素,size还没有等于0),
        //否则会出现ArrayIndexOutOfBoundsException
        if(front == dataElem.length)    
            front = 0;
        size--;
        return oldValue;
    }
    //得到队列顶端元素,即front指向的元素
    public AnyType get() {
        return dataElem[front];
    }
}

这版代码咋一看没什么问题,但实际上扩容操作即ensureCapacity方法有着极大问题,因为如果队列经过出队操作,那么满的时候front必在rear前面,即比rear更大,且大1。此时,如果对扩容之后的数组进行进队操作,将会覆盖掉前面已经存在的队列元素。问题如下图所示,
在这里插入图片描述
经过我一个小时的思考之后,我终于找到了解决办法
第二版代码(基本一样,就扩容操作发生了改变)

public class ArrayQueue<AnyType> {
    private AnyType[] dataElem;
    private int size;
    private static final int DEFAULT_CAPACITY = 10;
    private int front;
    private int rear;

    public ArrayQueue(){

        dataElem = (AnyType[]) new Object[DEFAULT_CAPACITY];
        size = 0;
        rear = -1;
        front = 0;

    }
    //扩容操作
    private void ensureCapacity(int newCapacity) {
        if(newCapacity <= size)
            return;
        AnyType[] oldDataElem = dataElem;
        dataElem = (AnyType[]) new Object[newCapacity];
        //新数组依然从0开始赋值
        //但旧数组从对头开始赋值,即front位置,
        //这样就保证了新数组的第一个元素必然是队头,最后一个元素必然是队尾,
        //方便了以后进队操作,而且时间复杂度基本没有变化
        for(int i = 0; i < size(); i++){   
            dataElem[i] = oldDataElem[front++];    
            if(front == oldDataElem.length)
                front = 0;
        }
        front = 0;
        rear = size() - 1;

    }
    public int size() {
        return size;
    }

    //入队
    public void enqueue(AnyType elem){
        if(size() == dataElem.length)
            ensureCapacity(size() * 2);
        if(++rear == dataElem.length)
            rear = 0;
        dataElem[rear] = elem;
        size++;
    }

    //出队
    public AnyType dequeue(){
        if(size() == 0)
            throw new NoSuchElementException();
        AnyType oldValue = dataElem[front++];
        if(front == dataElem.length)
            front = 0;
        size--;
        return oldValue;
    }
    //得到队列顶端元素,即front指向的元素
    public AnyType get() {
        return dataElem[front];
    }
}

好了,以上就是我的见解,如果有错,欢迎指正。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值