【Java/數據結構】隊列(Quque)

本博客將介紹隊列的相關知識,包括基于數組的普通隊列,基于鏈表的普通隊列,基于數組的雙端隊列,基于鏈表的雙端隊列,但不包括優先級隊列(PriorityQueue),此數據結構將單獨發一篇博客,那么,讓我們從基礎的隊列開始吧!

一.隊列概述

1.隊列概述

隊列是一種特殊的線性表,特殊之處在于它只允許在表的前端(front)進行刪除操作,而在表的后端(rear)進行插入操作,和棧一樣,隊列是一種操作受限制的線性表。進行插入操作的端稱為隊尾,進行刪除操作的端稱為隊頭。

可以把他想象成排隊買東西,先排隊先買。

特性:FIFO(First In First Out),先進先出。

Quque在Java集合框架中的位置如下:

雖然看起來很單一,但實際上隊列的實現相當豐富。

二.隊列的實現

我們要模擬實現是基于數組的循環隊列、基于鏈表的單端隊列、基于數組的雙端隊列、基于鏈表的雙端隊列。

1.源碼簡介

先看看源碼(可以直接看模擬實現):
首先看看Queue接口:

繼承自collection接口,提供了一些隊列基本方法,

還有繼承自Queue的Deque接口:

接下來看看AbstractQueue:

接下來是基本實現類:

有ArrayQueue:

LinkedList,沒錯,LinkedList也可以被當成隊列來使用:

LinkedList是繼承自Deque的,除此之外,還有像PriorityQueue(優先級隊列)、BlockingDeque(雙端阻塞隊列)等我們暫且不討論。

2.基于數組的循環隊列

(1)原理

為什么會加上循環特性呢?

原因是普通非循環隊列重復使用會造成嚴重的內存浪費,而加上循環特性后,就可以避免這一情況了,請看如下情況:

1、2出隊,5、6、7、8入隊后:

由于使用的是邏輯刪除,所以1、2所占內存其實還在,反復增刪后(內存不足自動擴容),就會變成一個有效數據極少,所占內存極大的隊列,與是,改進成循環隊列:

還是這張圖,但是當我們進行4、5出隊,9、10、11、12入隊的操作后,就開始循環存儲:

有效區域為6~12,當繼續添加元素13~20后,判斷隊尾會追上隊頭時,容量不足,于是會先把所有數據復制到內存擴大的新數組,并從0下標按順序排列后才會繼續添加元素:

先復制:

再添加元素:?

通常我們會用size與capacity來記錄容量是否滿出,即size>=capacity時自動擴容。

在設計時,要特別注意%處的操作。

(2)基本結構

public class MyQueue {private int[] queue;   // 存儲元素的數組private int front;     // 隊頭指針private int size;      // 當前元素數量private int capacity;  // 當前隊列容量private static final int DEFAULT_CAPACITY = 10;// 初始化隊列public MyQueue() {this.capacity = DEFAULT_CAPACITY;this.queue = new int[capacity];this.front = 0;this.size = 0;}......

說明:隊尾指針為 rear = (front + size) % capacity ,可以通過計算求得,不需要定義一個。

或者定義 rear,不定義size,都可以,此處隊尾指針表示的是隊尾元素的下一個節點,方便插入。

(3)擴容

// 隊列擴容
private void resize(int newCapacity) {int[] newArray = new int[newCapacity];// 將舊數組元素按順序復制到新數組頭部for (int i = 0; i < size; i++) {newArray[i] = queue[(front + i) % capacity];}queue = newArray;front = 0;       // 重置隊頭指針capacity = newCapacity; // 更新容量
}

(4)enqueue(入隊)

// 入隊操作(支持自動擴容)
public void enqueue(int item) {// 隊列已滿時觸發擴容if (isFull()) {resize(capacity * 2); // 容量翻倍}int rear = (front + size) % capacity;queue[rear] = item;size++;
}

(5)dequeue(出隊)

// 出隊操作
public int dequeue() {if (isEmpty()) {throw new IllegalStateException("隊列為空,無法出隊");}int item = queue[front];front = (front + 1) % capacity;size--;return item;
}

(6)peek(查看隊頭)

// 查看隊頭元素
public int peek() {if (isEmpty()) {throw new IllegalStateException("隊列為空");}return queue[front];
}

(7)其他

// 判斷隊列是否為空
public boolean isEmpty() {return size == 0;
}// 判斷隊列是否已滿
public boolean isFull() {return size == capacity;
}// 獲取當前隊列容量(測試用)
public int getCapacity() {return capacity;
}

(8)完整實現及測試代碼

為了測試,初始容量改為3:

public class MyQueue {private int[] queue;   // 存儲元素的數組private int front;     // 隊頭指針private int size;      // 當前元素數量private int capacity;  // 當前隊列容量(不再是final)private static final int DEFAULT_CAPACITY = 3;// 初始化隊列public MyQueue() {this.capacity = DEFAULT_CAPACITY;this.queue = new int[capacity];this.front = 0;this.size = 0;}// 入隊操作(支持自動擴容)public void enqueue(int item) {// 隊列已滿時觸發擴容if (isFull()) {resize(capacity * 2); // 容量翻倍}int rear = (front + size) % capacity;queue[rear] = item;size++;}// 出隊操作public int dequeue() {if (isEmpty()) {throw new IllegalStateException("隊列為空,無法出隊");}int item = queue[front];front = (front + 1) % capacity;size--;return item;}// 查看隊頭元素public int peek() {if (isEmpty()) {throw new IllegalStateException("隊列為空");}return queue[front];}// 隊列擴容private void resize(int newCapacity) {int[] newArray = new int[newCapacity];// 將舊數組元素按順序復制到新數組頭部for (int i = 0; i < size; i++) {newArray[i] = queue[(front + i) % capacity];}queue = newArray;front = 0;       // 重置隊頭指針capacity = newCapacity; // 更新容量}// 判斷隊列是否為空public boolean isEmpty() {return size == 0;}// 判斷隊列是否已滿public boolean isFull() {return size == capacity;}// 獲取當前隊列容量(測試用)public int getCapacity() {return capacity;}// 測試代碼public static void main(String[] args) {MyQueue queue = new MyQueue();// 初始容量3queue.enqueue(10);queue.enqueue(20);queue.enqueue(30);System.out.println("當前容量: " + queue.getCapacity()); // 3// 觸發擴容到6queue.enqueue(40);System.out.println("擴容后容量: " + queue.getCapacity()); // 6// 繼續填充測試queue.enqueue(50);queue.enqueue(60);queue.enqueue(70); // 再次觸發擴容到12System.out.println("再次擴容后容量: " + queue.getCapacity()); // 12// 驗證出隊順序System.out.print("出隊順序: ");while (!queue.isEmpty()) {System.out.print(queue.dequeue() + " "); // 10 20 30 40 50 60 70}}
}

3.基于鏈表的單端隊列

(1)原理

由于我們只對頭尾進行操作,所以我們可以用鏈表來實現,頭刪對應出隊,尾差對應入隊。

由于鏈式結構的存在,不必擔心內存不足與內存浪費,當然,一般情況下,由于創建多個對象節點,其效率不如數組實現高。

(2)實現

由于與鏈表類似,我們直接給出完整實現及測試用例:
?

public class LinkedListQueue {// 鏈表節點定義private static class Node {int data;Node next;Node(int data) {this.data = data;this.next = null;}}private Node front; // 隊頭指針(出隊位置)private Node rear;  // 隊尾指針(入隊位置)private int size;   // 隊列元素數量public LinkedListQueue() {front = null;rear = null;size = 0;}// 入隊操作(尾插)public void enqueue(int item) {Node newNode = new Node(item);if (isEmpty()) {// 隊列為空時,front和rear都指向新節點front = newNode;rear = newNode;} else {// 非空時,將新節點鏈接到隊尾,并更新rearrear.next = newNode;rear = newNode;}size++;}// 出隊操作(頭刪)public int dequeue() {if (isEmpty()) {throw new IllegalStateException("隊列為空,無法出隊");}int item = front.data;front = front.next; // 移動隊頭指針size--;// 如果出隊后隊列為空,需要同時更新rear為nullif (isEmpty()) {rear = null;}return item;}// 查看隊頭元素public int peek() {if (isEmpty()) {throw new IllegalStateException("隊列為空");}return front.data;}// 判斷隊列是否為空public boolean isEmpty() {return front == null;}// 獲取隊列元素數量public int size() {return size;}// 測試代碼public static void main(String[] args) {LinkedListQueue queue = new LinkedListQueue();// 入隊測試queue.enqueue(10);queue.enqueue(20);queue.enqueue(30);System.out.println("入隊后隊列大小: " + queue.size()); // 3// 查看隊頭System.out.println("當前隊頭: " + queue.peek()); // 10// 出隊測試System.out.println("出隊: " + queue.dequeue()); // 10System.out.println("出隊: " + queue.dequeue()); // 20System.out.println("出隊后剩余大小: " + queue.size()); // 1// 再次入隊queue.enqueue(40);System.out.println("新隊尾元素: " + queue.rear.data); // 40// 清空隊列測試System.out.println("出隊: " + queue.dequeue()); // 30System.out.println("出隊: " + queue.dequeue()); // 40System.out.println("隊列是否為空: " + queue.isEmpty()); // true// 測試空隊列異常try {queue.dequeue();} catch (IllegalStateException e) {System.out.println("異常捕獲: " + e.getMessage()); // 隊列為空,無法出隊}}
}

4.雙端隊列

(1)原理

雙端隊列(deque)是指允許兩端都可以進行入隊和出隊操作的隊列,deque 是 “double ended queue” 的簡稱。 那就說明元素可以從隊頭出隊和入隊,也可以從隊尾出隊和入隊。

其兩端都可出入的特性給了Deque很大的靈活性,使其基本代替了Stack,成為棧的主要實現類。

其也是實現滑動窗口的首選數據結構。

由于實現基本同上,我們直接給出實現及測試用例。

(2)基于數組的雙端隊列

public class MyArrayDeque {private int[] data;private int front;   // 隊頭指針private int size;    // 當前元素數量private int capacity; // 當前容量private static final int DEFAULT_CAPACITY = 8;// 初始化隊列(默認容量8)public MyArrayDeque() {this(DEFAULT_CAPACITY);}public MyArrayDeque(int initialCapacity) {capacity = initialCapacity;data = new int[capacity];front = 0;size = 0;}// 隊頭插入元素public void addFirst(int item) {if (isFull()) resize(capacity * 2);front = (front - 1 + capacity) % capacity; // 向前移動隊頭指針data[front] = item;size++;}// 隊尾插入元素public void addLast(int item) {if (isFull()) resize(capacity * 2);int rear = (front + size) % capacity;data[rear] = item;size++;}// 移除隊頭元素public int removeFirst() {if (isEmpty()) throw new IllegalStateException("Deque is empty");int item = data[front];front = (front + 1) % capacity;size--;return item;}// 移除隊尾元素public int removeLast() {if (isEmpty()) throw new IllegalStateException("Deque is empty");int rear = (front + size - 1) % capacity;int item = data[rear];size--;return item;}// 查看隊頭元素public int peekFirst() {if (isEmpty()) throw new IllegalStateException("Deque is empty");return data[front];}// 查看隊尾元素public int peekLast() {if (isEmpty()) throw new IllegalStateException("Deque is empty");return data[(front + size - 1) % capacity];}// 動態擴容private void resize(int newCapacity) {int[] newData = new int[newCapacity];// 將元素按順序復制到新數組頭部for (int i = 0; i < size; i++) {newData[i] = data[(front + i) % capacity];}data = newData;capacity = newCapacity;front = 0; // 重置隊頭指針}public boolean isEmpty() {return size == 0;}public boolean isFull() {return size == capacity;}public int size() {return size;}// 測試代碼public static void main(String[] args) {MyArrayDeque deque = new MyArrayDeque(3);// 隊頭插入deque.addFirst(10);deque.addFirst(20);deque.addFirst(30); // 觸發擴容到6// 隊尾插入deque.addLast(40);deque.addLast(50);deque.addLast(60); // 再次觸發擴容到12// 驗證順序System.out.println("隊頭移除: " + deque.removeFirst()); // 30System.out.println("隊尾移除: " + deque.removeLast());   // 60System.out.println("當前隊頭: " + deque.peekFirst());    // 20System.out.println("當前隊尾: " + deque.peekLast());     // 50}
}

(3)基于鏈表的雙端隊列

public class LinkedDeque {// 雙向鏈表節點定義private static class Node {int data;Node prev;Node next;Node(int data) {this.data = data;this.prev = null;this.next = null;}}private Node front;  // 隊頭指針private Node rear;   // 隊尾指針private int size;    // 隊列元素數量public LinkedDeque() {front = null;rear = null;size = 0;}// 隊頭插入元素public void addFirst(int item) {Node newNode = new Node(item);if (isEmpty()) {// 隊列為空時,front和rear都指向新節點front = rear = newNode;} else {// 將新節點鏈接到當前隊頭前newNode.next = front;front.prev = newNode;front = newNode; // 更新隊頭指針}size++;}// 隊尾插入元素public void addLast(int item) {Node newNode = new Node(item);if (isEmpty()) {front = rear = newNode;} else {// 將新節點鏈接到當前隊尾后rear.next = newNode;newNode.prev = rear;rear = newNode; // 更新隊尾指針}size++;}// 移除隊頭元素public int removeFirst() {if (isEmpty()) {throw new IllegalStateException("隊列為空,無法移除");}int item = front.data;if (front == rear) {// 只有一個元素時,移除后隊列為空front = rear = null;} else {front = front.next;front.prev = null; // 斷開舊隊頭鏈接}size--;return item;}// 移除隊尾元素public int removeLast() {if (isEmpty()) {throw new IllegalStateException("隊列為空,無法移除");}int item = rear.data;if (front == rear) {front = rear = null;} else {rear = rear.prev;rear.next = null; // 斷開舊隊尾鏈接}size--;return item;}// 查看隊頭元素public int peekFirst() {if (isEmpty()) {throw new IllegalStateException("隊列為空");}return front.data;}// 查看隊尾元素public int peekLast() {if (isEmpty()) {throw new IllegalStateException("隊列為空");}return rear.data;}public boolean isEmpty() {return size == 0;}public int size() {return size;}// 打印隊列內容(測試用)public void display() {Node current = front;System.out.print("隊列內容: ");while (current != null) {System.out.print(current.data + " ");current = current.next;}System.out.println();}// 測試代碼public static void main(String[] args) {LinkedDeque deque = new LinkedDeque();// 隊頭插入測試deque.addFirst(10);deque.addFirst(20);deque.display(); // 隊列內容: 20 10// 隊尾插入測試deque.addLast(30);deque.addLast(40);deque.display(); // 隊列內容: 20 10 30 40// 移除測試System.out.println("移除隊頭: " + deque.removeFirst()); // 20System.out.println("移除隊尾: " + deque.removeLast());  // 40deque.display(); // 隊列內容: 10 30// 邊界測試deque.removeFirst();deque.removeLast();System.out.println("隊列是否為空: " + deque.isEmpty()); // true// 異常測試try {deque.removeFirst();} catch (IllegalStateException e) {System.out.println("異常捕獲: " + e.getMessage());}}
}

5.其他隊列

  • 優先級隊列(PriorityQueue):其底層實現是堆,具體來說是完全二叉樹,我們會單獨開一篇博客分析。
  • 阻塞隊列(BlockingDeque):用于線程操作的數據結構。

三.隊列API的使用

Java當中基礎隊列有2種實現方式:

  • ArrayDeque
  • LinkedList

一般兩者可以相互替代,只是LinkedList適用于需要頻繁增刪的隊列,ArrayDeque適用于對訪問性能要求高的隊列,一般我們用ArrayDeque。

本博客重點在于理解其實現以及原理,詳細使用請詳見:Java ArrayDeque - Java教程 - 菜鳥教程

以及官方文檔:ArrayDeque (Java Platform SE 8 )

結語

隊列還是比較好理解和實現的,在理解了數據結構的底層原理以及實現后,我們才能更加清晰地正確使用數據結構,也能更好地學習后面的算法和原理了。

好了,本博客到此結束,喜歡不妨點個贊吧,我接下來會把剩下的數據結構一一解析完,下次是二叉樹,敬請期待!

我們下次見ξ( ?>??)!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/pingmian/72939.shtml
繁體地址,請注明出處:http://hk.pswp.cn/pingmian/72939.shtml
英文地址,請注明出處:http://en.pswp.cn/pingmian/72939.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

[數據結構]排序之 歸并排序(有詳細的遞歸圖解)

一、非遞歸 基本思想&#xff1a; 歸并排序&#xff08; MERGE-SORT &#xff09;是建立在歸并操作上的一種有效的排序算法 , 該算法是采用分治法&#xff08; Divide andConquer&#xff09;的一個非常典型的應用。將已有序的子序列合并&#xff0c;得到完全有序的序列&#x…

docker安裝向量數據庫Milvus及可視化工具 Attu

前置條件 1.安裝了docker 2.服務器網絡正常&#xff0c;可以連接到容器下載地址 3.服務器磁盤空間正常&#xff0c;docker磁盤占用過大&#xff0c;請參考docker容量占用過大解決辦法 一、下載yml文件 可在文章資源下載或者自行下載&#xff1a;下載yml 下載這個單機版本的…

科技云報到:AI Agent打了個響指,商業齒輪加速轉動

科技云報到原創。 3月16日&#xff0c;百度旗下文心大模型4.5和文心大模型X1正式發布。目前&#xff0c;兩款模型已在文心一言官網上線&#xff0c;免費向用戶開放。 同時&#xff0c;文心大模型4.5已上線百度智能云千帆大模型平臺&#xff0c;企業用戶和開發者登錄即可調用AP…

CSS 用于圖片的樣式屬性

CSS 設置圖像樣式 CSS中用于圖片的樣式屬性主要包括以下幾個方面&#xff1a; ?邊框和背景?&#xff1a; ?border?&#xff1a;可以設置圖片的邊框樣式、寬度和顏色。例如&#xff0c;img { border: 1px solid #ddd; } 會給圖片添加1像素的實線邊框&#xff0c;顏色為灰色…

EasyExcel--導入和導出Excel的方法

原文網址&#xff1a;EasyExcel--導入和導出Excel的方法_IT利刃出鞘的博客-CSDN博客 簡介 本文介紹SpringBoot整合EasyExcel導入和導出Excel的方法。 使用 Excel導入 實體類 Data public class OrderImportBO {ExcelProperty("訂單號")NotBlank(message "…

金融級安全加速:群聯SD-WAN如何兼顧防御與低延遲?

一、SD-WAN的核心價值 1. 傳統回源痛點 暴露風險&#xff1a;公網回源可能泄露源站IP&#xff0c;易遭針對性攻擊。延遲抖動&#xff1a;跨國業務因網絡擁堵導致延遲波動&#xff08;如金融交易超時&#xff09;。 2. 群聯方案優勢 加密專線&#xff1a;通過IPSec/SSL VPN建…

Apache Tomcat漏洞公開發布僅30小時后即遭利用

近日&#xff0c;Apache Tomcat曝出一項安全漏洞&#xff0c;在公開發布概念驗證&#xff08;PoC&#xff09;僅30小時后&#xff0c;該漏洞即遭到攻擊者利用。這一漏洞編號為CVE-2025-24813&#xff0c;主要影響以下版本&#xff1a; 1. Apache Tomcat 11.0.0-M1 至 11.0.2 …

計算機體系結構作業2

1 P108 有一條動態多功能流水線由5段組成(如圖3.35所示),加法用1、3、4、5段,乘法用1、2、5段,第2段的時間為2△t,其余各段的時間均為△t,而且流水線的輸出可以直接返回輸入端或暫存于相應的流水寄存器中。若在該流水線上計算 ∑ i 4 ( A i B i ) \sum_i^4(A_iB_i) ∑i4?(Ai…

python-leetcode 60.分割回文串

題目&#xff1a; 給定一個字符串S,請將S分割成一些子串&#xff0c;使每個子串都是回文串&#xff0c;返回S所有可能的分割方案 方法一&#xff1a;回溯深度優先搜索 1. 主要思想 使用 深度優先搜索&#xff08;DFS&#xff09; 遍歷 s 的所有可能劃分方式。使用 回溯&…

Java EE 進階:MyBatis

MyBatis是一個優秀的持久化框架&#xff0c;用于簡化JDBC的開發。 持久層就是持久化訪問的層&#xff0c;就是數據訪問層&#xff08;Dao&#xff09;&#xff0c;用于訪問數據庫的。 MyBatis使用的準備工作 創建項目&#xff0c;導入mybatis的啟動依賴&#xff0c;mysql的驅…

Go語言的基礎類型

一基礎數據類型 一、布爾型&#xff08;Bool&#xff09; 定義&#xff1a;表示邏輯真 / 假&#xff0c;僅有兩個值&#xff1a;true 和 false內存占用&#xff1a;1 字節使用場景&#xff1a;條件判斷、邏輯運算 二、數值型&#xff08;Numeric&#xff09; 1. 整數類型&…

【愚公系列】《高效使用DeepSeek》019-外語學習

??【技術大咖愚公搬代碼:全棧專家的成長之路,你關注的寶藏博主在這里!】?? ??開發者圈持續輸出高質量干貨的"愚公精神"踐行者——全網百萬開發者都在追更的頂級技術博主! ?? 江湖人稱"愚公搬代碼",用七年如一日的精神深耕技術領域,以"…

發布第四代液晶電視,TCL引領全新美學境界

在不斷革新的消費電子領域中&#xff0c;電視行業在視覺體驗上正面臨重要的美學挑戰。如何打破全面屏時代的物理束縛&#xff0c;將家居空間提升到“視覺無界”的層次&#xff0c;以及如何讓尖端技術更好地服務于影像沉浸感&#xff0c;成為行業關注的焦點。 3月10日&#xff…

劍指 Offer II 113. 課程順序

comments: true edit_url: https://github.com/doocs/leetcode/edit/main/lcof2/%E5%89%91%E6%8C%87%20Offer%20II%20113.%20%E8%AF%BE%E7%A8%8B%E9%A1%BA%E5%BA%8F/README.md 劍指 Offer II 113. 課程順序 題目描述 現在總共有 numCourses 門課需要選&#xff0c;記為 0 到 n…

【C++】STL庫面試常問點

STL庫 什么是STL庫 C標準模板庫&#xff08;Standard Template Libiary&#xff09;基于泛型編程&#xff08;模板&#xff09;&#xff0c;實現常見的數據結構和算法&#xff0c;提升代碼的復用性和效率。 STL庫有哪些組件 STL庫由以下組件構成&#xff1a; ● 容器&#xf…

【問題解決】Postman 測試報錯 406

現象 Tomcat 日志 org.springframework.web.servlet.handler.AbstractHandlerExceptionResolver.logException Resolved org.springframework.web.HttpMediaTypeNotAcceptableException: No acceptable representation HTTP狀態 406 - 不可接收 的報錯&#xff0c;核心原因 客…

第3節:AWK的特點和優勢

1 第3節&#xff1a;AWK的特點和優勢 AWK是一種功能強大的文本處理工具&#xff0c;具有以下特點和優勢&#xff1a; 1.1.1 簡潔性 AWK的語法簡潔明了&#xff0c;對于簡單的數據處理任務&#xff0c;通常只需編寫簡短的命令即可完成。例如&#xff0c;要從一個文本文件中提…

Flutter 打包 ipa出現錯誤問題 exportArchive

一、錯誤信息: Encountered error while creating the IPA: error: exportArchive: "Runner.app" requires a provisioning profile with the Push Notifications feature. Try distributing the app in Xcode: open /project/your_app/build/ios/archive/Runner.…

STC89C52單片機學習——第28節: [12-2] AT24C02數據存儲秒表(定時器掃描按鍵數碼管)

寫這個文章是用來學習的,記錄一下我的學習過程。希望我能一直堅持下去,我只是一個小白,只是想好好學習,我知道這會很難&#xff0c;但我還是想去做&#xff01; 本文寫于&#xff1a;2025.03.20 51單片機學習——第28節: [12-2] AT24C02數據存儲&秒表&#xff08;定時器掃…

Verilog-HDL/SystemVerilog/Bluespec SystemVerilog vscode 配置

下載 verible https://github.com/chipsalliance/verible的二進制包 然后配置 vscode