05-圖解資料結構之佇列--Queue
零、前言
棧是一種線性的資料結構
特性:尾部新增,頭部取出 即先進先出FIFO
操作:enqueue入隊 dequeue出隊 getFront檢視隊首元素

佇列.png
一、佇列介面
/** * 作者:張風捷特烈 * 時間:2018/8/17 0017:15:57 * 郵箱:[email protected] * 說明:佇列介面 */ public interface IQueue<T> { /** * 入隊 * @param el 元素 */ void enqueue(T el); /** * 出隊 * @return 元素 */ T dequeue(); /** * 獲取隊首元素 * @return 隊首元素 */ T getFront(); /** * 獲取佇列元素個數 * @return 元素個數 */ int getSize(); /** * 是否為空 * @return 是否為空 */ boolean isEmpty(); }
二、普通佇列的陣列實現
/** * 作者:張風捷特烈 * 時間:2018/8/17 0017:15:57 * 郵箱:[email protected] * 說明:佇列的陣列實現 */ public class ArrayGroupQueue<E> implements IQueue<E> { /** * 成員變數 */ private ArrayGroup<E> array; public ArrayGroupQueue(int capacity) { this.array = new ArrayGroup<>(capacity); } public ArrayGroupQueue() { this.array = new ArrayGroup<>(); } @Override public void enqueue(E el) { array.addLast(el); } @Override public E dequeue() { return array.removeFirst(); } @Override public E getFront() { return array.get(0); } @Override public int getSize() { return array.size(); } @Override public boolean isEmpty() { return array.isEmpty(); } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("IQueue :"); res.append("front [ "); for (int i = 0; i < array.size(); i++) { res.append(array.get(i)); if (i != array.size() - 1) { res.append(", "); } } res.append("] tail"); return res.toString(); } }
陣列普通佇列測試
/** * 陣列佇列測試 */ private static void arrayQueueTest() { ArrayGroupQueue<Integer> queue = new ArrayGroupQueue<>(); for (int i = 0; i < 5; i++) { queue.enqueue(i); System.out.println(queue); } queue.dequeue(); System.out.println(queue); //IQueue :front [ 0] tail //IQueue :front [ 0, 1] tail //IQueue :front [ 0, 1, 2] tail //IQueue :front [ 0, 1, 2, 3] tail //IQueue :front [ 0, 1, 2, 3, 4] tail //IQueue :front [ 1, 2, 3, 4] tail }
三、迴圈佇列
基於陣列實現的佇列在隊首取出是會使得整隊移動,而使時間複雜度為O(n)
可以使用迴圈隊,基於隊首隊尾兩個標示來固定佇列,從而使得時間複雜度為O(1)
迴圈佇列特點:為空時, 隊尾標示==隊首標示
,新加元素時,隊尾表識後移,
佇列滿: (隊尾標示+1)%陣列長度==隊首標示
迴圈佇列會使隊首前一個位置不可用。

迴圈佇列.png

迴圈佇列迴圈機制.png
/** * 作者:張風捷特烈 * 時間:2018/8/17 0017:16:03 * 郵箱:[email protected] * 說明:陣列實現迴圈佇列 */ public class ArrayLoopQueue<T> implements IQueue<T> { /** * 佇列資料 */ private T[] data; /** * 隊首標示 */ private int front; /** * 隊尾標示 */ private int tail; /** * 元素個數 */ private int size; /** * 無參構造:預設10個容量 */ public ArrayLoopQueue() { this(11); } /** * 一參構造 * * @param capacity 佇列容量 */ public ArrayLoopQueue(int capacity) { // 因為會有一個浪費,所以+1 data = (T[]) new Object[capacity + 1]; front = 0; tail = 0; size = 0; } @Override public void enqueue(T el) { if (isFull()) { grow(getCapacity() * 2); } data[tail] = el; //插入資料時對尾標示進行操作 tail = (tail + 1) % data.length; size++; } @Override public T dequeue() { if (isEmpty()) { throw new IllegalArgumentException("Cannot dequeue from an empty queue"); } T ret = data[front]; data[front] = null; //插入資料時對首標示進行操作 front = (front + 1) % data.length; size--; if (size == getCapacity() / 4 && getCapacity() / 2 != 0) { grow(getCapacity() / 2); } return ret; } @Override public T getFront() { if (isEmpty()) { throw new IllegalArgumentException("Cannot dequeue from an empty queue"); } return data[front]; } /** * 重新確定容量 * @param newCapacity 新的容量 */ private void grow(int newCapacity) { T[] newData = (T[]) new Object[newCapacity + 1]; for (int i = 0; i < size; i++) { // 此時在newData中隊首對齊回來,data中就得有一個front的偏移量 newData[i] = data[(i + front) % data.length]; } data = newData; front = 0; tail = size; } /** * 獲取容量 * @return 容量 */ public int getCapacity() { return data.length - 1; } /** * 佇列元素個數 * @return 元素個數 */ @Override public int getSize() { return size; } /** * 是否為空 * @return 是否為空 */ @Override public boolean isEmpty() { return front == tail; } /** * 佇列是否滿了 * @return 佇列是否滿了 */ public boolean isFull() { // tail的下一個位置等於front時 return (tail + 1) % data.length == front; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append(String.format("ArrayLoopQueue: size = %d, capacity = %d\n", size, getCapacity())); res.append("front ["); for (int i = front; i != tail; i = (i + 1) % data.length) { res.append(data[i]); if ((i + 1) % data.length != tail) // 最後一個元素不要加, res.append(", "); } res.append("] tail"); return res.toString(); } }
測試:
/** * 迴圈佇列測試 */ private static void LoopQueueTest() { ArrayLoopQueue<Integer> queue = new ArrayLoopQueue<>(8); for (int i = 0; i < 8; i++) { queue.enqueue(i); } System.out.println(queue); //ArrayLoopQueue: size = 8, capacity = 8 //front [0, 1, 2, 3, 4, 5, 6, 7] tail queue.dequeue(); queue.dequeue(); queue.dequeue(); queue.dequeue(); queue.enqueue(10); System.out.println(queue.getFront());//4 System.out.println(queue); //ArrayLoopQueue: size = 5, capacity = 8 //front [4, 5, 6, 7, 10] tail }
四、連結串列式集合實現佇列
連結串列和佇列可謂天然配,連結串列的頭刪除,頭獲取都是O(1),但尾新增是O(n),但可以維護首位標識,使尾加也為O(1)
/** * 作者:張風捷特烈 * 時間:2018/8/17 0017:22:50 * 郵箱:[email protected] * 說明:單鏈表實現佇列 */ public class SingleLinkedQueue<T> implements IQueue<T> { /** * 頭節點 */ private Node head; /** * 尾節點 */ private Node tail; /** * 元素個數 */ private int size; public SingleLinkedQueue() { head = null; tail = null; size = 0; } @Override public void enqueue(T el) { // 如果隊尾為空,說明佇列是空的。因為tail一直指向最後一個非空節點。 if (tail == null) { tail = new Node(null, el); head = tail; } else { // 使用tail.next把新Node掛載上來。 tail.next = new Node(null, el); // tail後挪 tail = tail.next; } size++; } @Override public T dequeue() { if (isEmpty()) throw new IllegalArgumentException("Cannot dequeue from an empty queue."); Node targetNode = head; head = head.next; // head後移 targetNode.next = null; // 元素置空 if (head == null) {// 如果頭結點為空 tail = null; } size--; return targetNode.el; } @Override public T getFront() { if (isEmpty()) throw new IllegalArgumentException("IQueue is empty."); return head.el; } @Override public int getSize() { return size; } @Override public boolean isEmpty() { return size == 0; } @Override public String toString() { StringBuilder res = new StringBuilder(); res.append("IQueue: front "); Node cur = head; while (cur != null) { res.append(cur + "->"); cur = cur.next; } res.append("NULL tail"); return res.toString(); } private class Node { public T el;//改節點上的元素 public Node next; //下一節點 /** * 兩參構造 * * @param next //下一節點 * @param el生成節點的元素值 */ public Node(Node next, T el) { this.el = el; this.next = next; } @Override public String toString() { return el.toString(); } } }
四、時間測試
陣列普通佇列:ArrayGroupQueue測試
方法\數量 | 複雜度 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|---|
enqueue | O(1) | 0.0006秒 | 0.0022秒 | 0.01571秒 | 0.06668秒 | 1.1375秒 |
dequeue | O(n) | 0.0111秒 | 0.2707秒 | 18.7684秒 | ---- | -- |
getFront | O(1) | -- | -- | -- | -- | -- |
陣列環形佇列:ArrayLoopQueue測試
方法\數量 | 複雜度 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|---|
enqueue | O(1) | 0.0004秒 | 0.0019秒 | 0.01775秒 | 0.05414秒 | 0.6896秒 |
dequeue | O(1) | 0.0005秒 | 0.0021秒 | 0.0091秒 | 0.0360秒 | 0.3327秒 |
getFront | O(1) | -- | -- | -- | -- | -- |
連結串列佇列:SingleLinkedStack測試
方法\數量 | 複雜度 | 1000 | 次10000次 | 10W次 | 100W次 | 1000次 |
---|---|---|---|---|---|---|
enqueue | O(1) | 0.0011秒 | 0.0031秒 | 0.0099秒 | 0.4881秒 | 3.1186秒 |
dequeue | O(1) | 0.0002秒 | 0.0013秒 | 0.0046秒 | 0.0221秒 | 0.1388秒 |
getFront | O(1) | -- | -- | -- | -- | -- |
後記、
1.宣告:
[1]本文由張風捷特烈原創,各圖均由本人親自所畫,轉載請註明
[2]歡迎廣大程式設計愛好者共同交流
[3]個人能力有限,如有不正之處歡迎大家批評指證,必定虛心改正
[4]你的喜歡與支援將是我最大的動力
2.連線傳送門:
ofollow,noindex">更多資料結構知識歡迎訪問:圖解資料結構 專案原始碼均在我的https://github.com/toly1994328/DS:歡迎star 張風捷特烈個人網站,程式設計筆記請訪問: http://www.toly1994.com
3.聯絡我
QQ:1981462002
微信:zdl1994328
4.歡迎關注我的微信公眾號,最新精彩文章,及時送達:

公眾號.jpg