数据结构 - 循环队列

循环队列

  在基于数组的队列中我们发现,在移出队首元素时时间复杂度为O(n),为了解决这个问题,我们引出了循环队列。

  

  实现原理:基于数组实现,以数组头为队首,以数组尾为队尾, 此时多出一个front变量指向队首,当队首元素移出时,基于数组实现的队列中的元素不需要全部向前移动一个位置,只需要指向下一个元素。

   实现循环队列的源码如下:

package queue;

public interface Queue<E> {
    
    public void enqueue(E e);
    
    public E dequeue();
    
    public E getFront();
    
    public int getSize();
    
    public boolean isEmpty();
}

package queue;

/**
 * 循环队列
 *
 * @author DELL
 *
 * @param <E>
 */
public class LoopQueue<E> implements Queue<E> {

        private E[] data;    
        private int front;     //指向队首的索引
        private int tail;    //指向队尾的索引
        private int size;    //队列中元素的个数
        
        /**
         * 构造方法
         * @param capacity
         * 队列的容量
         */
        public LoopQueue(int capacity){
            data = (E[])new Object[capacity];
            front = 0;
            tail = 0;
            size = 0;
        }
        
        /**
         * 无参构造方法,默认容量为0
         */
        public LoopQueue(){
            this(10);
        }
        
        /**
         * 获得队列的容量
         * @return
         */
        public int getCapacity(){
            return data.length;
        }
        
        /**
         * 判断队列是否为空
         */
        @Override
        public boolean isEmpty() {
            return size == 0;
        }
        
        /**
         * 获取队列里元素的个数
         */
        @Override
        public int getSize() {
            return size;
        }
        
        /**
         * 向队列的队尾添加一个元素
         */
        @Override
        public void enqueue(E e) {
            
            if (size == data.length) {
                resize(data.length * 2);
            }
            data[tail] = e;
            tail = (tail + 1) % data.length;
            size++;
        }
        
        /**
         * 将队首的元素移出并返回
         */
        @Override
        public E dequeue() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Cannot dequeue from an emtry queue.");
            }
            
            E res = data[front];
            data[front] = null;
            front = (front + 1) % data.length;
            size--;
            
            if(size == data.length / 4 && data.length / 2 != 0){
                resize(data.length / 2);
            }
        return res;
        }
        
        /**
         * 瞧一眼队首的元素
         */
        @Override
        public E getFront() {
            if (isEmpty()) {
                throw new IllegalArgumentException("Queue is empty.");
            }
            return data[front];
        }
        
        /**
         * 重置队列的容量
         * @param newCapacity
         */
        private void resize(int newCapacity) {
            E[] newData = (E[])new Object[newCapacity];
            for(int i = 0; i < size; i++){
                newData[i] = data[(i + front) % data.length];
            }
            
            tail = data.length;
            data = newData;
            front = 0;
            
            
            
        }
        
        @Override
        public String toString(){
            StringBuilder res = new StringBuilder();
            
            res.append(String.format("capacity = %d size = %d\n", getCapacity(),getSize()));
            res.append("Front: ");
            res.append('[');
            int j = front;  //遍历队列时记录指向队首的索引
            for (int i = 0; i < size; i++) {
                res.append(data[j]);
                if (i != getSize() - 1) {
                    res.append(", ");
                }
                j = (j + 1) % data.length;
            }
            res.append(']');
            res.append("tail");
            
            return res.toString();
        }
}

经典的实现循环队列实现中只用了front和tail变量,一个指向队首和一个指向队尾,size变量可以通过这两个变量推导出来,这种实现方式需要浪费一个空间,

当(front == tail时队列为空,(tail+1) % data.length == front队列满)。

循环队列的时间复杂度的分析:

  *void enqueue(E e) : O(1) 均摊

  *E dequeue()  : O(1) 均摊

  *E getFront() : O(1)

  *E getSize() : O(1)

  *boolean isEmpty() : O(1)

猜你喜欢

转载自www.cnblogs.com/tc971121/p/10013125.html