Redis中常見的五種資料型別(一)
佇列簡介
佇列是一種特殊的線性表,只允許在表的前端(front)進行刪除操作,表後端(rear)進行插入操作。進行刪除操作的位置叫做對頭,進行插入操作的位置叫做隊尾。
特性
FIFO-First In First Out,先進先出儲存資料。
佇列實現主要分為順序佇列和迴圈佇列。
- 順序佇列
建立順序佇列結構必須為其靜態或者動態分配一片的連續儲存空間,並且設定兩個指標進行管理,一個是對頭指標front,一個是隊尾指標rear。
初始化佇列時,rear=front=0,每次新增元素,rear增1;每次刪除元素,front增1。隨著新增和刪除操作的依次進行,佇列元素的個數不斷變化,佇列所佔用的儲存空間也在為佇列結構所分配的空間中移動。當rear增加到指向分配的連續空間之外時,佇列無法在插入元素,但這時往往還有大量的可用空間未被佔用,這些空間是已經出隊的佇列元素曾經佔用過的儲存單元。
順序佇列的溢位情況:
1)“下溢”現象:當佇列為空時,做出隊運算產生的溢位現象。“下溢”是正常現象,常用於程式控制轉義的條件。
2)“真上溢”現象:當佇列滿時,做入隊運算產生空間溢位的現象。“真上溢”是一種出錯狀態,應設法避免。
3)“假上溢”現象:由於入隊和出隊操作中,頭尾指標只增加不減少,導致被刪除元素所佔用過的空間無法被重新利用。當佇列中實際的元素個數小於儲存空間規模時,也可能由於尾指標已經到達儲存空間的上界點而不能做入隊操作。該現象稱之為“假上溢”現象。
圖示
順序佇列java程式碼陣列實現
點選檢視程式碼
/** * 資料實現順序佇列,包含isFull()、isEmpty()、insert()、remove()、peekFront()基本操作。 * @date 2021/12/24 */ @Data @Slf4j public class SequentialQueue { /** 佇列最大值 */ private int maxSize; /** 陣列實現 */ private Object[] queueArray; /** 隊頭 */ private int front; /** 隊尾 */ private int rear; public SequentialQueue(int maxSize) { this.maxSize = maxSize; this.queueArray = new Object[maxSize]; this.front = 0; this.rear = 0; } /** 佇列是是否已滿 */ public boolean isFull() { return rear == maxSize; } /** 佇列是否為空 */ public boolean isEmpty() { return rear == 0; } /** 入隊 */ public void insert(Object object) { // 隊尾是否是最大值 boolean isFull = this.isFull(); if (isFull) { log.error("佇列已滿,入隊失敗。"); } else { queueArray[rear] = object; rear++; } } /** 出隊,刪除資料,對頭後移 */ public void remove() { if (this.isEmpty()) { log.error("佇列已空,出隊失敗。"); } else { queueArray[front] = null; front++; } } /** 出隊,刪除資料,對頭後移 */ public Object peekFront() { return this.queueArray[front]; } public static void main(String[] args) { SequentialQueue queue = new SequentialQueue(3); queue.insert(10); queue.insert(23); queue.insert(34); log.info("入隊後對頭值,num = {}", queue.peekFront()); queue.remove(); log.info("出隊後對頭值,num = {}", queue.peekFront()); } }
- 迴圈佇列
在實際使用佇列時,為了能使佇列空間得到重複使用,往往會對佇列的使用方法稍加改進。無論插入或刪除操作,一旦rear指標或front指標增加1時超過了所分配的儲存空間,就讓它們指向儲存空間的起始位置。指標從maxSize-1增1變為0,可用取餘運算rear%maxSize和front%maxSize來實現。
實際上就是把佇列空間想象成環形空間,在空間中的儲存單元迴圈使用,用這種方法管理的佇列就是迴圈佇列。除了一些簡單應用之外,真正實用的佇列就是迴圈佇列。
圖示
迴圈佇列Java資料實現
點選檢視程式碼
/** * 陣列實現迴圈佇列,包含insert()、remove()、peekFront()、isFull()和isEmpty()方法。 * @author Yoko */ @Data @Slf4j public class LoopQueue { /** 隊頭 */ private int front; /** 隊尾 */ private int rear; /** 實現陣列 */ private Object[] queueArray; /** 陣列長度 */ private int maxSize; /** 實際資料量 */ private int nItem; public LoopQueue(int maxSize) { this.maxSize = maxSize; this.queueArray = new Object[maxSize]; this.front = 0; this.rear = 0; this.nItem = 0; } /** 佇列是否為空 */ public boolean isEmpty() { return this.nItem == 0; } /** 佇列是否已滿 */ public boolean isFull() { return this.nItem == this.maxSize; } /** 入隊 */ public void insert(Object object) { if (this.isFull()) { log.info("佇列已滿,入隊失敗。"); } else { this.queueArray[rear] = object; this.rear = (this.rear + 1) % this.maxSize; this.nItem++; } } /** 出隊 */ public void remove() { if (this.isEmpty()) { log.info("佇列已空,出隊失敗。"); } else { this.queueArray[front] = null; this.front = (this.front + 1) % this.maxSize; this.nItem--; } } /** 檢視隊頭的值 */ public Object peekFront() { if (!this.isEmpty()) { return this.queueArray[front]; } return null; } public static void main(String[] args) { LoopQueue queue = new LoopQueue(4); queue.insert(155); queue.insert(10); queue.insert(2); log.info("入隊後資料:loopArray = {}", queue); log.info("對頭的值:front= {}", queue.peekFront()); queue.remove(); log.info("出隊後,queueArray = {}", queue); log.info("出隊後,對頭的值:front= {}", queue.peekFront()); queue.insert(223); queue.insert(15); queue.insert(190); log.info("再次入隊後資料:loopArray = {}", queue); } }