數據結構之「隊列」

什麼是隊列?

隊列(queue)是隻容許在一端進行插入操做,而在另外一端進行刪除操做的線性表。是一種先進先出(First In First Out)的線性表,簡稱 FIFO。容許插入的一端稱爲隊尾,容許刪除的一端稱爲隊頭。
隊列有 2 種方式來存儲:數組 和 鏈表。
數組咱們都知道它是預先分配好長度的,所以會出現溢出現象,並且刪除元素須要向隊頭移動一個位置,時間複雜度就變成 O(n)。所以,須要一種新的方式來解決這個問題,那就是循環隊列。
隊列的這種頭尾相接的順序存儲結構稱爲循環隊列。爲了不隊列刪除元素須要移動整個隊列,使得隊頭和隊尾能夠在數組中循環變化。解決了移動元素的時間損耗,使得原本刪除是 O(n) 的時間複雜度變成了 O(1)。java

1. 數組式隊列

申請一片連續的存儲空間,並設置兩個指針進行管理。一個是隊頭指針front,它指向隊頭元素;另外一個是隊尾指針rear,它指向下一個入隊元素的存儲位置。通常是實現循環隊列,隊頭和隊尾會隨着入隊和出隊的變化而變化的。例如 JDK 中 ArrayBlockingQueue 就是基於循環隊列來實現的。不過它會有溢出現象,通常解決方案是若是隊列滿了,設置入隊等待時間或者返回入隊不成功。通常在肯定元素個數狀況下使用,若是不肯定元素個數,建議使用鏈表式隊列。
數組式隊列數組

2. 鏈表式隊列

它就是基於鏈表存儲結構的隊列,能夠動態的建立和刪除元素,不用關心隊列的長度,所以不用擔憂溢出現象。新元素插入到隊尾,讀取的時候從隊頭開始,每次讀取一個元素,釋放一個元素,這就是所謂的動態建立和動態刪除。
鏈表式隊列微信

隊列有什麼用?

1. 保證輸入順序

好比吃飯排隊,先找服務員拿個號碼,上面會寫着前面還有 n 桌,這就至關於服務員把你加入了她們店的隊列中,當有空位置時,就直接叫你入座吃飯,當沒有空位子時,要麼排隊等候,要麼換一家吃飯。這就是隊列的用處。this

2. 解耦

在系統設計中,好的設計是低耦合,高內聚。意思就是一個系統只作一件事情,把一件事情作好。既方便代碼維護,又方便擴展。好比又一個下單的場景,用戶下單以後須要加積分,須要給各類優惠券等等。咱們就能夠利用隊列來解偶,但真實使用通常是用開源的消息隊列,如Rocket MQ,Kafka 等等。spa

3. 提高系統吞吐量

就拿上面訂單來講,原本之前是用戶下單,給用戶添加積分,給用戶發優惠券是一塊兒處理成功後返回的。如今只是處理下單,而後告訴某某系統,給某某用戶加積分,給某某用戶發優惠券了,它本身並不真正作這個動做,因此會提高系統響應,當須要保證最終一致性。通常也是用開源的消息隊列來完成的。設計

隊列怎麼實現?

這裏是基於數組的循環隊列實現,也就是 JDK 的 ArrayBlockingQueue。有興趣的朋友也能夠看看基於鏈表的 LinkedBlockingQueue 的實現。
存儲結構指針

public class ArrayBlockingQueue<E> {
    //用數組來存儲元素
    Object[] items;
    //數組裏出隊的下標
    int takeIndex;
    //入隊的下標
    int putIndex;
    //元素個數
    int count;
}

入隊code

public boolean offer(E e) {
    //加入數組滿了,則返回入隊失敗
    if (count == items.length) {
        return false;
    } else {
        //得到當前數組
        Object[] items = this.items;
        //把元素 e 加入到隊尾
        items[putIndex] = e;
        //判斷是否是隊尾是否是倒數第二個元素
        //是的話把下標爲 0 置爲隊尾,說明一圈了
        if (++putIndex == items.length) {
            putIndex = 0;
        }
        //總數加一
        count++;
        return true;
    }
}

出隊隊列

public E poll() {
    //空隊列則返回 null
    if (count == 0) {
        return null;
    } else {
        //獲取當前數組
        Object[] items = this.items;
        //獲取隊頭元素
        E item = (E) items[takeIndex];
        //置空
        items[takeIndex] = null;
        //重置隊頭爲下標 0;
        if (++takeIndex == items.length) {
            takeIndex = 0;
        }
        //總數減一
        count--;
        return item;
    }
}

總結

隊列的做用仍是很大的,好比保證輸入順序,解偶,提高系統吞吐量等等,都基於隊列的原理來實現的。隊列有數組式隊列和鏈表式隊列。數組式隊列就是基於數組存儲結構來實現的,數組的優缺點它全有。鏈表式隊列就是基於鏈表存儲結構來實現的,它也包含來鏈表的優缺點。因此在使用時能夠根絕業務需求來選擇最優的方案。rem

PS:
清山綠水始於塵,博學多識貴於勤。
我有酒,你有故事嗎?
微信公衆號:「清塵閒聊」。
歡迎一塊兒談天說地,聊代碼。

相關文章
相關標籤/搜索