Java 循環隊列/環形隊列的實現流程

之前,我們使用鏈表實現瞭基礎隊列,鏈接放在這裡可以去康康喲

Java棧和基礎隊列的實現詳解

之所以沒有選擇數組來實現,是因為每有一個元素出隊,數組中所有剩下的元素都需要向前移動一次,因此沒有鏈表高效。

此時,我們就引出瞭循環隊列的概念。

循環隊列,又稱環形隊列,邏輯上是一個環,物理上依然是線性表。

head-指向隊列的第一個元素

tail-指向隊列最後一個元素的下一個位置

當tail走到數組末尾時,下一步再次返回數組頭部(從0開始)

出隊之後的元素就訪問不到瞭,此時邏輯上已經將它刪除瞭,tail向後走到該位置時覆蓋它即可。此時就解決瞭數組隊列需要一直搬運元素的問題。

瞭解循環隊列的概念之後,我們就能明白它的幾個基礎知識:

當head == tail 時,表示隊列為空

根據上面 head 和 tail 的定義,隊列已滿時,tail 指向最後一個元素的下一個位置,也就是head ,此時我們就無法區分環形隊列到底是已滿還是為空。所以我們在環形隊列的數組中浪費一個空間,在區分數組是否已滿,如下圖,就是一個已滿的隊列

那麼如何移動 tail 指針使他返回數組頭部呢?我們運用取模操作:

tail = ( tail + 1 ) % 數組.length; 

根據上式我們就能判斷隊列是否已滿的方法:

( tail + 1 ) % 數組.length == head; 

此時 head 和 tail 引用向後移動時,不能簡單的 +1,要 ++ 之後對數組長度取模,這樣就可以返回數組頭部繼續向後移動:

 head = ( head +1 ) % 數組.length;

tail = ( tail +1 ) % 數組.length;

那麼如何獲取當前隊列最後一個元素的索引呢?

圖①中最後一個元素的索引就是 tail -1

圖②的tail恰好在數組第一個位置,這時最後一個元素下標就是 數組.length -1

綜上:數組最後一個元素的索引lastIndex

lastIndex = tail == 0 ? 數組.length -1 : tail -1;

代碼實現:

public interface Queue<E> {
    void offer(E val);//入隊
    E poll();//出隊,返回出隊的值
    E peek();//返回隊首元素
    boolean isEmpty();//判斷是否為空
}
 
 
import java.util.NoSuchElementException;
 
//基於整型的循環隊列
public class LoopQueue implements Queue<Integer>{
    private Integer[] data;
    private int head;//隊首元素
    private int tail;//隊尾元素的下一個位置
    private int size;//當前元素個數
    public LoopQueue(int n){
        //n為希望保存的元素個數
        //在循環隊列中要浪費一個元素空間,來判斷是否已滿,所以 +1
        data = new Integer[n + 1];
        }
 
    @Override
    //添加元素
    public void offer(Integer val) {
        if(isFull()){
            throw new ArrayIndexOutOfBoundsException("queue is full, cannot offer new val");
        }
        data[tail] = val;
        //tail向後移
        tail = (tail + 1) % data.length;
        size++;
    }
 
    @Override
    //出隊
    public Integer poll() {
        if (isEmpty()){
            throw new NoSuchElementException("queues empty!cannot poll");
        }
        Integer val = data[head];
        //head向後移動
        head = (head + 1) % data.length;
        size--;
        return val;
    }
 
    @Override
    public Integer peek() {
        if (isEmpty()){
            throw new NoSuchElementException("queues empty!cannot poll");
        }
        return data[head];
    }
 
    @Override
    //判斷是否為空
    public boolean isEmpty() {
        return tail == head;
    }
    public String toString(){
        StringBuilder sb = new StringBuilder();
        sb.append("front[");
        //取得最後一個元素的下標
        int lastIndex = tail == 0 ? data.length-1 : tail-1;
        for (int i = head; i != tail; ) {
            sb.append(data[i]);
            if (i != lastIndex){
                sb.append(",");
            }
            i = (i+1) % data.length;
 
        }
        sb.append("]tail");
        return sb.toString();
    }
    //判斷隊列是否已滿
    public boolean isFull(){
        return (tail + 1) % data.length == head;
    }
}
//代碼測試
public class LoopQueueTest {
    public static void main(String[] args) {
        Queue<Integer> loopQueue = new LoopQueue(5);
        for (int i = 0; i < 5; i++) {
            loopQueue.offer(i+1);
        }
        System.out.println(loopQueue);
//        loopQueue.offer(1);
        System.out.println(loopQueue.poll());
        System.out.println(loopQueue);
    }
}

運行結果:

到此這篇關於Java 循環隊列/環形隊列的實現流程的文章就介紹到這瞭,更多相關Java 循環隊列內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: