深入淺出 棧和隊列(附加循環隊列、雙端隊列)

棧和隊列

  • 一、棧 概念與特性
  • 二、Stack 集合類及模擬實現
    • 1、Java集合中的 Stack
    • 2、Stack 模擬實現
  • 三、棧、虛擬機棧、棧幀有什么區別?
  • 四、隊列 概念與特性
  • 五、Queue集合類及模擬實現
    • 1、Queue的底層結構
      • (1)順序結構
      • (2)鏈式結構
    • 2、Java集合中的 Queue
    • 3、Queue 模擬實現
  • 六、設計循環隊列
    • 1、循環隊列涉及的兩個關鍵問題
      • (1)問題 1 解決方案
      • (2)問題 2 解決方案
    • 2、循環隊列具體實現
  • 七、Deque 集合類

一、棧 概念與特性

一種特殊的線性表,其只允許在固定的一端進行插入和刪除元素操作。進行數據插入和刪除操作的一端稱為棧
頂,另一端稱為棧底。棧中的數據元素遵守后進先出 LIFO(Last In First Out)的原則。

二、Stack 集合類及模擬實現

1、Java集合中的 Stack

在這里插入圖片描述

在Java集合中 Stack 集合類可以表示棧,Stack繼承了Vector,Vector和ArrayList類似,都是動態的順序表,因此Stack 底層維護的也是一個動態順序表。

我們可以先看一下Stack中給出的常用方法:

構造方法

方法解釋
Stack()構造一個空的棧

常用方法

方法功能
E push(E e)將元素 e 入棧,并返回 e
E pop()將棧頂元素出棧并返回
E peek()獲取棧頂元素
int size()獲取棧中有效元素個數
boolean empty()檢測棧是否為空

2、Stack 模擬實現

有了順序表和鏈表的基礎,棧的實現就非常的簡單了,下面就直接給出順序棧的實現代碼,大家可以參照注釋進行理解:

public class MyStack {// 數組public int[] elem;// 棧頂public int top;// 構造方法(初始化順序棧)public MyStack() {elem = new int[5];}//1.壓棧:E push(E e) //檢測容量private boolean isFull() {return top == elem.length;}public int push(int e) {if (isFull()) {// 如果棧滿就擴容elem = Arrays.copyOf(elem,2*elem.length);}elem[top++]=e;return e;}//2.出棧:E pop() public int pop() {// 如果棧空,拋異常(這里可以自定義實現一個異常類)if (empty()) {throw new EmptyException("空棧!");}// 正常出棧return elem[--top];}//3.獲取棧頂元素:E peek() public int peek() {return elem[top-1];}//4.獲取棧中有效元素個數:int size() public int size() {return top;}//5.檢測棧是否為空:boolean empty() public boolean empty() {return top == 0;}
}

上面實現的 棧 底層是一個動態數組,它的入棧和出棧都達到了O(1)。其實棧的底層也可以實現成鏈表的結構:

(1) 底層為單向鏈表。 如果底層維護一個單鏈表,那么可以將入棧實現為頭插;出棧實現為頭刪;這樣以來它的時間復雜的也可以達到O(1)。

(2)底層為雙向鏈表。 對于雙向鏈表來說,由于同時記錄了head結點和last結點,并且每個結點的前后結點都是明確的,故無論是在頭部使用插入刪除模擬入棧出棧,還是在尾部實現,復雜度都可以達到O(1)。

這一點在Java集合類LinkedList中給出的接口方法中,得到了體現,它里面就提供了push()pop()peek()等方法。

三、棧、虛擬機棧、棧幀有什么區別?

  1. 棧是一種數據結構,用于存儲和管理數據。
  2. 虛擬機棧是JVM在執行Java程序時使用的一塊內存區域,包含了線程的棧幀。
  3. 棧幀是虛擬機棧中的一個元素,用于存儲方法調用的信息。

四、隊列 概念與特性

只允許在一端進行插入數據操作,在另一端進行刪除數據操作的特殊線性表,隊列具有先進先出FIFO(First In First Out) 的特點。

進行插入操作的一端稱為隊尾(Tail/Rear),進行刪除操作的一端稱為隊頭(Head/Front)

五、Queue集合類及模擬實現

1、Queue的底層結構

眾所周知,無論是棧也好,隊列也好,它們的本質都是一個容器,既然是容器那么底層就有兩種結構:順序結構、鏈式結構。那么隊列的底層究竟是用 順序結構還是鏈式結構?如果使用鏈式結構,那么是使用單向還是雙向呢?下面我們一起來探討一下:

(1)順序結構

我們知道,隊列的兩個關鍵操作是,入隊和出隊。如果使用順序結構,那么意味著它的底層將維護一個數組,此時我們有兩種實現方案:

  • 方案一: 使用數組的頭插完成入隊,使用數組的尾刪實現出隊
  • 方案二: 使用數組的尾插完成入隊,使用數組的頭刪實現出隊

方案一分析:使用方案1,入隊的話使用頭插法,需要挪動數組元素,復雜度為O(N);出隊的復雜度可以達到O(1)

方案二分析:使用方案2,入隊復雜度可達到O(1),而出隊,由于使用的頭刪,每次需要移動大量的元素,復雜度達到了O(N)。所以這兩種方案均不能保證入隊和出隊的復雜度達到O(1),因此不建議采用。但是可通過循環數組,解決上述問題(后面講解)

(2)鏈式結構

  • 方案一:使用單向鏈表。
  • 方案二:使用雙向鏈表。

方案一分析:如果采用方案1使用單向鏈表,我們需要定義兩個特殊結點 :頭結點-head,尾結點-last。由于鏈表是單向的,所以如果此時我們采用頭插表示入隊,復雜度可達到O(1);尾刪表示出隊,由于鏈表是單向的,所以還需要找到 last 結點的前一個結點,復雜度達到了O(N)。所以需要使用尾插表示入隊,復雜度可達到O(1);頭刪表示出隊,復雜度也可達到O(1).

方案二分析:如果采用方案2使用雙向鏈表。由于鏈表是雙向的,所以 無論是將頭插表示出隊,尾刪表示入隊;還是尾插表示入隊,頭刪表示出隊都可以達到復雜度為O(1).

2、Java集合中的 Queue

并且在Java集合中的Queue接口底層實現的就是一個雙向鏈表:

下面我們先查看一下Queue接口中一些常見的隊列方法:

方法功能
boolean offer(E e)入隊列,將元素插入隊列尾部
E poll()出隊列,移除并返回隊頭元素
E peek()獲取隊頭元素,但不移除
int size()獲取隊列中有效元素的個數
boolean isEmpty()檢測隊列是否為空

3、Queue 模擬實現

同樣根據上面給出的隊列方法,下面就實現一個底層為雙向鏈表的隊列,大家可以參照上述實現思路和代碼注釋進行理解:

//原理:先進先出
//思路:雙向鏈表頭尾無所謂,下面采用[頭插、尾刪]代替[入隊、出隊]
public class DQueue {// 定義雙向鏈表節點static class Node {public int val;public Node prev;public Node next;public Node(int val) {this.val = val;}}// 頭結點public Node head;// 尾結點public Node last;// 隊列長度public int size;//1.入隊列:boolean offer(E e) 【頭插】public boolean offer(int e) {Node node = new Node(e);// 判空if (isEmpty()) {head = node;last = node;} else {// 頭插node.next = head;head.prev = node;head = head.prev;}size++;return true;}//2.出隊列:E poll() 【尾刪】public int poll() {// 判空if (isEmpty()) {// 這里可以自定義異常throw new EmptyException("隊列為空!");}// 接收出隊元素int ret = head.val;// 如果只有1個結點if (head.next == null) {last = null;head = null;} else {// 大于1個結點last.prev.next = null;last = last.prev;}size--;return ret;}//3.獲取隊頭元素:int peek() public int peek() {// 判空if (isEmpty()) {// 這里可以自定義異常throw new EmptyException("隊列為空!");}return head.val;}//4.獲取隊列中有效元素個數:int size() public int getSize() {return size;}//5.檢測隊列是否為空:boolean isEmpty() public boolean isEmpty() {return size == 0;}
}

六、設計循環隊列

循環隊列通過巧妙地利用數組的循環使用,解決了普通隊列在頭部插入和刪除操作時效率低下的問題,使得offer()poll()操作均能夠在O(1)的時間復雜度內完成。這種結構經常在 生產者消費者模型(敬請期待) 中使用。

1、循環隊列涉及的兩個關鍵問題

循環隊列的設計主要涉及兩個關鍵問題:

  1. 數組下標如何循環?
  2. 如何判斷循環隊列的空與滿?

(1)問題 1 解決方案

方案一: headtail 使用加 1 取模操作。既保證當 head 和 tail 不處于數組尾下標時,加 1 取模操作不影響下標正常遞增,又能保證 head 和 tail 處于數組尾下標時繼續 入隊 或 出隊 下標循環。

方案二: 判斷 tailhead 與 數組長度 nums.length 是否相等,相等則置 0 保證下標循環。

(2)問題 2 解決方案

方案一: 設置 usedSize 變量記錄隊列元素個數。usedSize 為 0 隊列為空;usedSize 等于數組長度 nums.length 隊列為滿。

方案二: 犧牲 1 個數組元素。當 head 等于 tail 時表示隊列為空;當 tail + 1 等于 head 時表示隊列為滿。

2、循環隊列具體實現

解決了上面兩個問題,下面的實現就是信手拈來。下面給出具體的實現代碼,大家可以參照注釋理解(判空 / 滿 :這里采用 useSize;循環:使用 head / tail 與 nums.length 判斷):

// 規定有效元素為:[head,tail)
// 判空判滿使用:usedSize
// 下標循環使用:head / tail 與 nums.length 判斷
public class CircularQueue {// 數組private int[] elem;// 隊頭private int head;// 隊尾的下一個private int tail;// 有效元素個數private int usedSize;// 使用構造方法,初始化循環隊列public CircularQueue(int k) {this.elem = new int[k];this.head = 0;this.tail = 0;this.usedSize = 0;}// 1.入隊列:put()public boolean put(int val) {// 判滿if (isFull()) {return false;}// 正常入隊情況elem[tail ++] = val;// 增加有效元素個數usedSize ++;// 判斷下標if (tail == elem.length) {tail = 0;}return true;}// 2.出隊列:take()public int take() {// 判空if (isEmpty()) {throw new EmptyException("隊列為空!");}// 正常出隊情況int val = elem[head ++];// 減少有效元素個數usedSize --;// 判斷下標if (head == elem.length) {head = 0;}return val;}// 3.獲取隊頭元素:peekHead()public int peekHead() {// 判空if (isEmpty()) {throw new EmptyException("隊列為空!");}int val = elem[head --];return val;}// 4.獲取隊尾元素:peekTail()public int peekTail() {// 判空if (isEmpty()) {throw new EmptyException("隊列為空!");}// 由于 tail 指向最后一個有效元素的下一個,所以需要考慮下標邊界情況int index = tail == 0? elem.length -1:tail -1;int val = elem[index];return val;}// 5.判空:isEmpty()public boolean isEmpty() {return usedSize == 0;}// 6.判滿:isFull()public boolean isFull() {return usedSize == elem.length;}// 7.獲取隊列有效元素個數:getSize()public int getSize() {return usedSize;}
}

七、Deque 集合類

Deque表示一個雙端隊列,即允許兩端都可以進行入隊和出隊操作的隊列。

在Java集合框架中,Deque是一個接口,它實現了LinkedListArrayDeque,分別表示雙端隊列的線性實現 和 雙端隊列的鏈式實現。其中線性實現的底層為一個循環數組,鏈式實現的底層為一個雙向鏈表。

在實際工程中,使用Deque接口是比較多的,Deque接口中涵蓋了隊列和棧的功能接口,實現棧和隊列均可以使用該接口:

Deque<Integer> stack1 = new ArrayDeque<>();  // 線性實現
Deque<Integer> stack2 = new LinkedList<>();  // 鏈式實現Deque<Integer> queue1 = new ArrayDeque<>();  // 線性實現
Deque<Integer> queue2 = new LinkedList<>();  // 鏈式實現

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

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

相關文章

Golang-使用 gvm 進行版本控制

當你想為每個項目切換 go 版本時&#xff0c;gvm (Go Version Manager) 很方便。 這里&#xff0c;我將介紹“如何在Mac上安裝gvm”和“如何使用gvm” 使用準備 僅適用于 Mac 的準備工作 按照MacOSX 要求中的說明執行以下命令。 xcode-select --install brew update brew …

C++(Qt)軟件調試---將調試工具安裝到AeDebug(11)

C(Qt)軟件調試—將調試工具安裝到AeDebug&#xff08;11&#xff09; 文章目錄 C(Qt)軟件調試---將調試工具安裝到AeDebug&#xff08;11&#xff09;1、前言1.1 使用的調試工具 2、調試器安裝1.1 WinDbg1.2 procdump1.3 DrMinGW1.4 vsjitdebugger 更多精彩內容&#x1f449;個…

深入了解Linux運維的重要性與最佳實踐

Linux作為開源操作系統的代表&#xff0c;在企業級環境中的應用越來越廣泛。而在保障Linux系統的正常運行和管理方面&#xff0c;Linux運維顯得尤為關鍵。本文將介紹Linux運維的重要性以及一些最佳實踐&#xff0c;幫助讀者更好地了解和掌握Linux系統的運維技巧。 首先&#xf…

OPENCV C++(十)gramm矯正+直方圖均衡化

兩者都是只對單通道使用&#xff0c;對多通道的話 就需要分離通道處理再合并通道 兩種方法&#xff0c;第一個要運算次數太多了&#xff0c;第二個只需要查表 伽馬矯正函數&#xff0c;這里用第二種方法&#xff0c;且寫法有點高級 int gammaCorrection(cv::Mat srcMat, cv::…

Java【Spring】使用注解, 更簡單的存儲和獲取 Bean

文章目錄 前言一、存儲 Bean1, 配置文件2, 五大類注解Bean 的命名規則 3, 方法注解Bean 的命名規則 二、獲取 Bean1, 屬性注入2, Setter 注入3, 構造方法注入4, Autowired 和 Resource 的區別5, 同一個類型的多個 Bean 注入問題 總結 前言 各位讀者好, 我是小陳, 這是我的個人主…

【網絡基礎實戰之路】實現RIP協議與OSPF協議間路由交流的實戰詳解

系列文章傳送門&#xff1a; 【網絡基礎實戰之路】設計網絡劃分的實戰詳解 【網絡基礎實戰之路】一文弄懂TCP的三次握手與四次斷開 【網絡基礎實戰之路】基于MGRE多點協議的實戰詳解 【網絡基礎實戰之路】基于OSPF協議建立兩個MGRE網絡的實驗詳解 PS&#xff1a;本要求基于…

FreeRTOS(任務通知)

資料來源于硬件家園&#xff1a;資料匯總 - FreeRTOS實時操作系統課程(多任務管理) 目錄 一、任務通知的概念 1、概念 2、發送通知給任務的方式 3、任務通知使用限制 二、任務通知的運行機制 三、任務通知的API函數 1、任務通知的數據結構 2、常用的API函數 3、函數x…

opencv實戰項目 手勢識別-實現尺寸縮放效果

手勢識別系列文章目錄 手勢識別是一種人機交互技術&#xff0c;通過識別人的手勢動作&#xff0c;從而實現對計算機、智能手機、智能電視等設備的操作和控制。 1. opencv實現手部追蹤&#xff08;定位手部關鍵點&#xff09; 2.opencv實戰項目 實現手勢跟蹤并返回位置信息&…

Linux elasticsearch設置為開機自啟動服務

Linux elasticsearch怎么設置為設置為開機自啟動服務 1、進入/etc/init.d目錄 cd /etc/init.d 2、新建文件elasticsearch&#xff0c;注意&#xff0c;沒有擴展名 vi elasticsearch 3、新建文件elasticsearch的內容如下 說明&#xff1a; &#xff08;1&#xff09;“su…

基于低代碼和數字孿生技術的電力運維平臺設計

電力能源服務商在為用能企業提供線上服務的時候&#xff0c;不可避免要面對用能企業的各種個性化需求。如果這些需求和想法都要靠平臺廠家研發人員來實現&#xff0c;那在周期、成本、效果上都將是無法滿足服務運營需要的&#xff0c;這也是目前很多線上能源云平臺應用效果不理…

【狀態模式】拯救if-else堆出來的屎山代碼

前言 我想大家平時都在開發重都遇見過屎山代碼&#xff0c;這些屎山代碼一般都是由于復雜且龐大的if-else造成的&#xff0c;狀態模式&#xff0c;是一種很好的優化屎山代碼的設計模式&#xff0c;本文將采用兩個業務場景的示例來講解如何使用狀態模式拯救屎山代碼。 目錄 前…

【Axure高保真原型】通過輸入框動態控制環形圖

今天和大家分享通過輸入框動態控制環形圖的原型模板&#xff0c;在輸入框里維護項目數據&#xff0c;可以自動生成對應的環形圖&#xff0c;鼠標移入對應扇形&#xff0c;可以查看對應數據。使用也非常方便&#xff0c;只需要修改輸入框里的數據&#xff0c;或者復制粘貼文本&a…

簡單記錄牛客top101算法題(初級題C語言實現)BM17 二分查找 BM21 旋轉數組的最小數字 BM23 二叉樹的前序遍歷

1. BM17 二分查找 要求&#xff1a;給定一個 元素升序的、無重復數字的整型數組 nums 和一個目標值 target &#xff0c;寫一個函數搜索 nums 中的 target&#xff0c;如果目標值存在返回下標&#xff08;下標從 0 開始&#xff09;&#xff0c;否則返回 -1。 輸入&#xff1a…

【云原生】K8S存儲卷:PV、PVC詳解

目錄 一、emptyDir存儲卷二、hostPath存儲卷三、nfs共享存儲卷四、PVC 和 PV4.1 NFS使用PV和PVC4.2創建動態PV 一、emptyDir存儲卷 容器磁盤上的文件的生命周期是短暫的&#xff0c;這就使得在容器中運行重要應用時會出現一些問題。首先&#xff0c;當容器崩潰時&#xff0c;ku…

UG NX二次開發(C++)-PK函數創建一條圓弧曲線

文章目錄 1、前言2、創建一個項目3、添加頭文件4、在do_it中添加創建圓曲線的源代碼5、調用dll6、再創建一個長方體驗證1、前言 采用PK進行UG NX二次開發,現在看到的文章很多是直接創建實體,然后在UG NX的視圖區顯示出來,對于創建圓曲線的文章不多,本文講一下PK函數創建圓…

Java基礎篇--日期時間類

目錄 前言 Instant&#xff08;時間戳&#xff09;類 LocalData(日期)類 LocalTime(時間)類 LocalDataTime(日期時間)類 Duration(時間間隔)類 Period(日期間隔)類 Clock&#xff08;獲取時區&#xff09;類 前言 在開發中經常需要處理日期和時間&#xff0c;Java提供…

Git 代碼分支規范

目的 俗話說&#xff1a;沒有規矩&#xff0c;不成方圓。遵循一個好的規章制度能讓你的工作事半功倍。同時也可以展現出你做事的認真的態度以及你的專業性&#xff0c;不會顯得雜亂無章&#xff0c;管理困難。Git分支規范也是一樣。當遵循了某種約定的Git分支&#xff0c;在代…

若依框架淺淺介紹

由若依官網所給介紹可知 1、文件結構介紹 在ruoyi-admin的pom.xml文件中引入了ruoyi-framework、ruoyi-quartz和ruoyi-generatior模塊&#xff0c;在ruoyi-framework的pom.xml文件中引入了ruoyi-system模塊。 2、技術棧介紹 前端&#xff1a;Vue、Element UI后端&#xff1a…

Redis持久化機制簡介

當涉及到Redis的持久化時&#xff0c;有兩種主要的持久化方式&#xff1a;RDB&#xff08;Redis Database&#xff09;快照和AOF&#xff08;Append-Only File&#xff09;日志。這些方式可以根據需求的不同&#xff0c;選擇適合的策略。 RDB&#xff08;Redis Database&#…

第1章:緒論

科學、技術、工程、應用 科學&#xff1a;是什么、為什么技術&#xff1a;怎么做工程&#xff1a;怎樣做的多快好省應用&#xff1a;怎么使用 定義 機器學習&#xff1a;利用經驗改善系統自身的性能。 研究 智能數據分析&#xff08;數據分析算法&#xff09; 典型的機器…