《Java數據結構和演算法》- 棧和隊列

来源:https://www.cnblogs.com/fireway/archive/2018/04/24/8925280.html
-Advertisement-
Play Games

Q: 棧、隊列與數組的區別? A: 本篇主要涉及三種數據存儲類型:棧、隊列和優先順序隊列,它與數組主要有如下三個區別: A: (一)程式員工具 數組和其他的結構(棧、隊列、鏈表、樹等等)都適用於資料庫應用中作為數據記錄。它們常用於記錄那些對應於現實世界的對象和活動的數據,如職員檔案等,這些結構便於數據 ...


Q: 棧、隊列與數組的區別?

A: 本篇主要涉及三種數據存儲類型:棧、隊列和優先順序隊列,它與數組主要有如下三個區別:

A: (一)程式員工具 
數組和其他的結構(棧、隊列、鏈表、樹等等)都適用於資料庫應用中作為數據記錄。它們常用於記錄那些對應於現實世界的對象和活動的數據,如職員檔案等,這些結構便於數據的訪問:它們易於進行插入、刪除和查找特定數據項的操作。 
然而,本篇要講解的數據結構和演算法更多的是作為程式員的工具來運用。它們主要作為構思演算法的輔助工具,而不是完全的數據存儲工具。這些數據結構的生命周期比那些資料庫類型的結構要短得多。在程式操作執行期間它們才被創建,通常用它們去執行某項特殊的任務;當完成任務之後,它們則被銷毀。

A: (二)受限訪問 
在數組中,若知道數據項的下標,便可以立即訪問該數據項;而在本篇的數據結構中,訪問是受限制的,即在特定時刻只有一個數據項可以被讀取或者刪除。 
這些結構介面的設計增強了這種受限訪問,訪問其他數據項理論上是不允許的。

A: (三)更加抽象 
棧、隊列和優先隊列是比數組和其他數據存儲結構更為抽象的結構。主要是通過介面對棧、隊列和優先隊列進行定義,介面表明瞭它們可以完成的操作,而主要實現機制對用戶來說是不可見的。 
例如:棧的實現機制可以用數組實現,本篇的示例就是這樣處理的,但它也可以用鏈表來實現。優先順序隊列的內部實現可以用數組或一種特別的樹-堆來實現。

Q: 什麼是棧?

A: 棧只允許訪問一個數據項:即最後插入的數據項。移除這個數據項才能訪問倒數第二個插入的數據項,以此類推。

Q: 棧有哪些實際場景?

A: 大部分微處理器運用基於棧的體繫結構,當調用一個方法時,把它的返回值和參數壓入棧,當方法結束返回時,哪些數據出棧。棧操作就嵌入在微處理器中。

Q: 棧的Java代碼?

A: 在本例中,StackX類裡面數據項的類型為long型,是用數組來實現,top變數存儲棧頂元素的下標。

示例: StackX.java, StackXTest.java

Q: 棧示例1:單詞逆序?

A: 棧的第一個例子是做一件非常簡單的事情:單詞逆序。因為棧的後進先出(LIFO)的特點,所以字母的順序就顛倒了。

示例:Reverser.java

Q: 棧示例2:分隔符匹配?

A: 棧通常用於匹配左分隔符和右分隔符,因為最後出現的左邊分隔符需要最先匹配,這個規律符合棧的LIFO的特點。 
下麵是一些例子:

c[d]        // correct
a{b[c]d}e   // correct
a{b(c]d}e   // not correct; ] doesn’t match (
a[b{c}d]e}  // not correct; nothing matches final }
a{b(c)      // not correct; nothing matches opening {

分隔符匹配程式從字元串中不斷地讀取字元,每次讀取一個字元。若發現它是左分隔符,將它壓入棧中。當讀到一個右分隔符時,彈出棧頂的左分隔符,並且查看它是否和右分隔符向匹配。(註:非分隔符的字元不插入棧中,只需忽略它們。)

示例:BracketChecker.java

Q: 棧的效率?

A: StackX類中實現的棧,入棧和出棧的時間複雜度為常數O(1), 棧操作所消耗的時間不依賴於棧中數據項的個數,因此操作時間很短。棧不需要比較和移動操作。

Q: 什麼是隊列?

A: 隊列(Queue)在詞典里是“排隊”(waiting line)的意思。電腦科學中,隊列是一種數據結構,有點類似棧,只是隊列中的第一個插入的數據項最先被移除(先進先出,FIFO),而在棧中,最後插入的數據項最先移除(LIFO)。

Q: 什麼是迴圈隊列?

A: 為了避免隊列不滿卻不能插入新數據項的問題,可以讓隊頭隊尾指針繞過到數組開始的位置,這就是迴圈隊列。

Q: 隊列有哪些實際的場景?

A: 使用文字處理器,敲擊一個鍵,而電腦又暫時要做其他的事情。敲擊的內容不會被丟失,它會在隊列中等待,知道文字處理程式有時間來讀取它。利用隊列保證了鍵入內容在處理時其順序不會改變。

Q: 隊列的Java代碼?

A: Queue類不但有mFront(隊頭)和mRear(隊尾),還有隊列當中當前數據項的個數mSize。

A: add/offer()方法 
將指定的元素插入此隊列。區別在於add(e)會拋出異常,offer(e)返回特殊值。

內部調用insert()方法,一般情況,插入操作mRear(隊尾指針)加一後,在隊尾指針所指的位置處插入新的數據項。但是,當mRear指向數組的最後一個元素,即mItems.length - 1位置的時候,在插入數據項之前,它必須回到數組的第一個元素前面,即mRear設置為-1,因此當mRear加1後,它等於0,是數組第一個元素的下標值。最後,mSize加一。

A: remove()/poll()方法 
獲取並移除此隊列的頭。區別在於remove(e)會拋出異常,poll(e)返回特殊值。

內部調用extract()方法,該方法總是由mFront指針得到隊頭數據項的值,然後將mFront加一。但是,如果這樣做mFront會超過數組的大小,mFront必須繞回到數組下標為0的位置上。註意先將返回值臨時存儲起來。最後mSize減一。

A: peek()方法 
獲取但不移除此隊列的頭;如果此隊列為空,則返回NULL_ELEMENT。

A: 示例:Queue.java

Q: 沒有數據項計數欄位的隊列Java代碼?

A: 在Queue類中包含數據項計數欄位mSize會使insert()和extract()方法增加一點額外的操作,因為處理insert()和remove()方法必須分別遞增和遞減這個變數值。這可能算不上額外的開銷,但是如果處理大量的插入和移除操作,這就可能會影響性能。因此,一些隊列的實現不使用數據項計數的欄位,而是通過mFront和mRear來計算出隊列是否為空或者滿以及數據項的個數。

示例:Queue.java

Q: 隊列的效率?

A: 和棧一樣,隊列中插入數據項和移除數據項的時間複雜度均為O(1)

Q: 雙端隊列?

A: 

Q: 優先順序隊列?

A: 優先順序隊列是不同於先進先出隊列的另一種隊列。每次從隊列中取出的是具有最高優先權的元素。

Q: 優先順序隊列有哪些實際場景?

A: 例如,在搶先式多任務操作系統中,程式排列在優先順序隊列中,這樣優先順序最高的程式會得到時間片並得以運行。

Q: 優先順序隊列的Java代碼?

A: 本示例使用有序數組實現優先順序隊列,這種方式插入比較慢,但是它比較簡單,適用於數據量比較小並且不是特別註重插入速度的情況。

示例:PriorityQueue.java

A: 最小關鍵字值得數據項總是數組的高端(最高下標值處),而最大的數據項在低端(下標值為0的位置)。 

A: 待出隊的數據項總是在數組的高端,所以刪除操作又快又容易。刪除數據項後,隊頭指針下移指向隊列新的高端,不需要移動和比較。

A: 註意沒有指針迴繞,隊尾指針從不移動,它總是指著數組低端的元素。

A: Font和Rear指針是為了和普通隊列做比較,實際上並不需要它們。

A: 在數據項個數比較少,或不太關心速度的情況下,用數組實現優先順序隊列還可以滿足要求。如果數據項很多,或速度很重要時,採用堆是更好的選擇。

Q: 優先順序隊列的效率?

A: 本示例的優先順序隊列插入操作需要O(N)時間,而刪除操作則需要O(1)的時間。後面將介紹如何通過堆來改進插入操作的時間。

Q: 如何解析算術表達式?

A: 對於諸如 2*(3+4) 或者 ((2+4)*7)+3*(9-5)的算術表達式,前面我們已經演示了怎麼應用棧來檢查分隔符是否匹配正確,但是接下來我們將學習如何解析這些算術表達式。

事實上,對電腦演算法來說如果要直接求算術表達式的值還是相當困難,因此對於演算法來說分這兩步實現更容易: 
1. 將算術表達式轉換成另一種形式:尾碼表達式 
2. 計算尾碼表達式的值

Q: 中綴/尾碼表達式?

A: 中綴表達式:操作符放在兩個操作數之間。比如A+B或A/B等。 
A: 尾碼表達式:也稱作逆波蘭表達式(Reverse Polish Notation或者RPN),它是由一位波蘭的數學家發明的。操作符跟在兩個操作數的後面,而且不包含括弧。這樣A+B 就變成AB+,A/B變成AB/。

A: 下麵是中綴表達式轉化為尾碼表達式的例子: 

Q: 人類是如何計算中綴表達式?

A: 由於Mr. Klemmer的長期的教學教育,對於3+4+5或者3*(4+5)表達式,我們人類做起這個問題卻相當容易。通過分析人類如何計算這些表達式的值,可以得出轉化為尾碼表達式的啟示: 
粗略地說,解析算術表達式時,應該遵循下列幾條規則: 
1. 從左到右讀取算式 
2. 已經讀到了可以計算值得兩個操作數和一個操作符時,就可以計算,並用計算結果代替那兩個操作數和哪個操作符 
3.繼續這個過程--從左到右,能算就算--直到表達式的結尾。

A: 示例:3 + 4 - 5

 

A: 示例: 3 + 4 * 5 
 

A: 示例: 3 * (4 + 5) 
 

Q: 如何把中綴表達式轉換成尾碼表達式?

A: 正如前面看到的那樣,計算中綴表達式,既要向前,也要向後讀表達式。將中綴表達式轉換成尾碼表達式的規則和計算中綴表達式的規則類似。但是還是有點變化。

A: 將中綴表達式轉換成尾碼表達式不用做算術運算,它不求中綴表達式的值,只是把操作數和操作符重新排列成另一種形式。

A: 轉換規則: 

A: 示例:A+B-C 

A: 示例:A+B*C 

A: 示例:A*(B+C) 

Q: 中綴表達式轉換成尾碼表達式的Java代碼 ?

A: 示例: In2PostTransform.java

Q: 人類如何用尾碼表達式求值?

A: 下圖展示了人類是如何通過觀察和鉛筆在紙上計算尾碼表達式的。 
示例:345+*612+/– 

從左邊第一個操作符開始,把它和它左邊相鄰的兩個操作數畫在一個圓圈裡,然後應用操作符運算兩個操作數並把結果寫在圓圈裡。最大的圓圈裡算出來的值就是這個表達式最後的結果。

Q: 尾碼表達式求值的規則?

A: 怎麼才能寫一個程式來重覆剛纔的求值過程呢?正如上面所描述,每遇到一個操作符,就用它來運算在這之前最後看到的兩個操作數,這就表明可以利用棧來存儲操作數。它的規則如下: 

算術操作的結果被壓入到棧中,在最後一個字元(一定是操作符)被讀取並執行計算以後,棧里只剩一個數據項,它就是整個表達式的運算結果。

Q: 尾碼表達式求值的Java代碼?

A: 為了簡化代碼,這裡限制只能輸入一位數的數字。 
示例:PostfixParser.java

Q: 本篇小結?

  • 棧、隊列和優先順序隊列是經常用於簡化某些程式操作的數據結構。
  • 在這些數據結構中,只有一個數據項可以被訪問。
  • 棧只允許訪問最後一個插入的數據項。
  • 隊列只允許訪問第一個插入的數據項。
  • 隊列可以實現為迴圈隊列,它基於數組,數組下標可以從數組末端迴繞到數組的開始位置。
  • 優先順序隊列的重要操作是有序地插入新數據項和移除關鍵字最小(有時是最大)的數據項。
  • 這些數據結構可以用數組實現,也可以用其他機制(例如鏈表)來實現。

Q: 參考

    1. 《Java數據結構和演算法》Robert Lafore 著,第4章 - 棧和隊列

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 概述 理解柯里化函數,需要有閉包的基礎,只有徹底理解閉包後才能理解柯里化,如果尚未理解閉包,建議閱讀上文js引擎的執行過程(一);如果理解了閉包再研究柯里化函數,則會大大的加深你對閉包理解,並且更清楚的認識到閉包的應用場景,那麼如果在面試時候問到閉包,你就可以侃侃而談了;並且理解柯里化函數會在很大的 ...
  • 當代碼在執行環境中執行時,會創建一個作用域鏈。作用域鏈本質是一個指向變數對象的指針列表。 如果執行環境是函數,則將其活動對象(最開始時只包含一個變數->argument對象)作為變數對象。ps:argument對象在全局環境中是不存在的. (基於2條件下)作用域鏈中的下一個變數對象來自外部環境,而再 ...
  • 概述 js是一種非常靈活的語言,理解js引擎的執行過程對我們學習javascript非常重要,但是網上講解js引擎的文章也大多是淺嘗輒止或者只局部分析,例如只分析事件迴圈(Event Loop)或者變數提升等等,並沒有全面深入的分析其中過程。所以我一直想把js執行的詳細過程整理成一個較為詳細的知識體 ...
  • Document ...
  • 百度一下WebRTC,我想也是一堆。本以為用SkyRTC-demo 就可以一馬平川的實現聊天,結果折騰了半天,文本信息都發不出去,更別說視頻了。網上的SimpWebRTCDemo,WebRTC-Experiment等對於第一次部署的人來說,都是相當的蛋疼。於是親自踩坑填坑,完美實現! ...
  • 阿裡發佈了<<阿裡巴巴Java開發手冊終極版>>,也許看過後也不能完全吸收,我在這裡分類整理,方便大家在手機端查看,一起學習阿裡對Java工程師編程的規約。 註釋規約 1. 【強制】類、類屬性、類方法的註釋必須使用 Javadoc 規範,使用/**內容*/格式,不得使用// xxx 方式。 說明:在 ...
  • 1. Ubuntu16.04上使用sudo apt-get install php7.1 安裝php的預設路徑如下: a. php可執行命令:/usr/bin/php7.1 和 /usr/bin/php b. 需要安裝sudo apt install php7.1-dev 才會有 /usr/bin/ ...
  • 導讀: 1.變數和對象 2.可變對象與不可變對象 3.引用傳參 在C/C++中,傳值和傳引用是函數參數傳遞的兩種方式。由於思維定式,從C/C++轉過來的Python初學者也經常會感到疑惑:在Python中,函數參數傳遞是傳值,還是傳引用呢?看下麵兩段代碼: 看完第一段代碼,會有人說這是值傳遞,因為函 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...