標籤:區分 img defaults 位置 兩種 無法 default 出隊 連續
定義
隊的操作是在兩端進行,一端只能進行插入操作(入隊),稱為隊尾,一端只能進行刪除操作(出隊),稱為隊尾。
隊列的運算規則是FIFO(first in first out)。
隊列的入隊、出隊操作分別具有入隊和出隊的指標,通常以f(front) 表示隊首指標,r(rear)表示隊尾指標。
隊列的儲存具有順序儲存和鏈式儲存兩種方式。
基本運算:
- 初始化;
- 判斷空;
- 判斷滿;
- 入隊;
- 出隊;
- 取出隊首元素。
package com.wuwii.test;/** * 隊列 * @author Zhang Kai * @version 1.0 * @since <pre>2017/12/17 13:12</pre> */public interface Queue<E> { /** * 入隊 * @param element 入隊元素 */ void enqueue(E element); /** * 出隊第一個元素 * @return 第一個元素 */ E dequeue(); /** * 返回隊首的元素 * @return E 隊首元素 */ E frontValue(); /** * 清空隊列的所有元素 */ void clear(); /** * 判斷隊列是否為空白 * @return true 為空白, */ Boolean isEmpty();}
順序隊列
。由於順序儲存結構的空間是連續的,需要一個向量空間儲存當前隊列的所有元素,由於隊列的對頭和隊尾的位置是變化的。初始化的時候,rear 和front 都設定為 0 ;入隊的時候將新元素插入rear 所指的位置,然後將rear 加 1 ;出隊的時候,刪除 front 所指的元素,然後將 front 加 1並返回該元素。由此可見,當頭尾指標相等時隊列為空白。在非空隊列中,頭指標始終指向隊頭元素,而尾指標始終指向隊尾元素的下個位置。
這裡其實存在一個問題需要解決。隊列的順序儲存結構同樣也存在上溢和下溢的問題。而且,隊列中還存在假溢出現象,指的是隊列在入隊和出隊的操作中,頭尾指標不斷增加而不減少或者只減少而不增加,導致被刪除的元素的空間無法重新利用,因此,儘管隊列中實際的元素個數遠遠小於向量空間的規模,但也可能由於尾指標已遠遠超越向量空間的上界而不能進行入隊或出隊的操作,這種現象稱為假上溢。
解決假上溢有多種方法。
- 固定頭指標:固定隊首指標,一旦刪除元素,需要移動所有元素,然後修改隊尾指標,這樣就又可以插入元素,只有在不能插入元素的時候,隊列才是滿的,否則可以一直插入元素,但是也存在一個,每次刪除元素的時候,都移動了大量的資料,造成效能損失;
- 迴圈向量:儲存在迴圈向量的隊列成為迴圈隊列。
迴圈隊列
儲存在迴圈向量的隊列成為迴圈隊列
假設向量的空間為m,只要在入隊或者出隊操作的時候,將隊首和隊尾的指標對m做求模運算即可實現隊首和隊尾指標的迴圈,因此,隊首和隊尾指標的數值取值範圍為0 ~ m-1 之間。
入隊時:rear = (rear + 1) % maxSize;
出隊時:front = (front + 1) % maxSize;
入隊時,rear指標不斷加一,知道rear 指標追上 front 指標的時候,隊列滿,rear = front,出隊的時候,只需要判斷 front = rear。因此,又出現了一個問題,判斷隊列的空與滿的條件相同。
區分隊列的空與滿:
- 方式一:浪費一個空間;rear指向剛剛插入的位置,front指向剛剛插入的位置;入隊的時候先不修改rear 指標,先去判斷
(rear + 1) % maxSize = front,成立,表示隊列已經滿了。出隊的時候只需要判斷 front = rear,不為空白的話,刪除front = (front + 1) % maxSize位置的元素。
- 方式二:設定一個變數來儲存儲存的元素個數;將該變數跟隊列最大容量進行比較,如果相等,則隊列滿,如果該變數為0,表示隊列為空白。
上面兩種方式都需要消費一個儲存單元。
實現迴圈隊列需要兩個指標的開銷,需要定義數組的最大下標範圍,遵循先進先出原則。
package com.wuwii.test;/** * 順序隊列 -》迴圈隊列 * @author Zhang Kai * @version 1.0 * @since <pre>2017/12/17 13:17</pre> */public class Squeue<E> implements Queue<E> { /** * 隊列預設大小 */ private final int defaultSize = 10; /** * 佇列儲存體的元素個數 */ private int size; /** * 隊首位置 */ private int front; /** * 隊尾位置 */ private int rear; /** * 儲存數組 */ private Object[] elements; /** * 無參構造,使用預設大小10 */ public Squeue() { initQueue(defaultSize); } /** * 有參構造,指定隊列大小 * @param givenSize 需要指定隊列大小 */ public Squeue(int givenSize) { initQueue(givenSize); } /** * 入隊 * * @param element 入隊元素 */ @Override public void enqueue(E element) { checkFull(); rear = (rear + 1) % size; elements[rear] = element; } /** * 出隊第一個元素 * * @return 第一個元素 */ @Override public E dequeue() { checkEmpty(); front = (front + 1) % size; return (E) elements[front]; } /** * 返回隊首的元素 * * @return E 隊首元素 */ @Override public E frontValue() { return (E) elements[(front + 1) % size]; } /** * 清空隊列的所有元素 */ @Override public void clear() { if (isEmpty()) { return; } front = rear = 0; } /** * 判斷隊列是否為空白 * * @return true 為空白, */ @Override public Boolean isEmpty() { return front == rear; } /** * 初始化隊列 * @param givenSize 給定大小 */ private void initQueue(int givenSize) { size = givenSize + 1; front = rear = 0; elements = new Object[size]; } /** * 檢查隊列滿 * 隊列滿了拋出異常 */ private void checkFull() { if (front == (rear + 1) % size) { throw new RuntimeException("Queue is full"); } } /** * 檢查隊列為空白 * 為空白拋出異常 */ private void checkEmpty() { if (isEmpty()) { throw new RuntimeException("Queue is empty"); } }}
鏈式隊列
相比順序隊列,鏈式隊列的隊首和隊尾改為指標。
鏈式隊列存在一個很方便的特點,只需要一個隊尾指標就可以完成隊列所有的基本運算,主要原因就是隊列的運算方式時先進先出,已知一個任意節點,就可以尋找出所有,所以只需要一個隊尾就可以了,節約空間開銷。
清空隊列只需要將指標刪除即可。
package com.wuwii.test;/** * 鏈式隊列,迴圈隊列 * @author Zhang Kai * @version 1.0 * @since <pre>2017/12/17 14:14</pre> */public class LinkQueue<E> implements Queue<E> { /** * 隊首指標 */ private Node<E> front; /** * 隊尾指標 */ private Node<E> rear; public LinkQueue() { initQueue(); } /** * 入隊 * * @param element 入隊元素 */ @Override public void enqueue(E element) { if (rear == null) { rear = new Node<>(null, element); } else { front = rear = new Node<>(null, element); } } /** * 出隊第一個元素 * * @return 第一個元素 */ @Override public E dequeue() { checkEmpty(); E e = front.element; front = front.next; return e; } /** * 返回隊首的元素 * * @return E 隊首元素 */ @Override public E frontValue() { return front.element; } /** * 清空隊列的所有元素 */ @Override public void clear() { front = rear = null; } /** * 判斷隊列是否為空白 * * @return true 為空白, */ @Override public Boolean isEmpty() { return rear == null; } /** * 儲存單元,鏈表單元 * @param <E> */ private static class Node<E> { Node<E> next; E element; Node(Node<E> next, E element) { this.next = next; this.element = element; } } /** * 初始化隊列 */ private void initQueue() { front = rear = null; } /** * 檢查隊列為空白 * 為空白拋出異常 */ private void checkEmpty() { if (isEmpty()) { throw new RuntimeException("Queue is empty"); } }}
Java中隊列