1. 程式人生 > >資料結構 - 迴圈佇列

資料結構 - 迴圈佇列

迴圈佇列

  在基於陣列的佇列中我們發現,在移出隊首元素時時間複雜度為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)