《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
  • 概述:在C#中,++i和i++都是自增運算符,其中++i先增加值再返回,而i++先返回值再增加。應用場景根據需求選擇,首碼適合先增後用,尾碼適合先用後增。詳細示例提供清晰的代碼演示這兩者的操作時機和實際應用。 在C#中,++i 和 i++ 都是自增運算符,但它們在操作上有細微的差異,主要體現在操作的 ...
  • 上次發佈了:Taurus.MVC 性能壓力測試(ap 壓測 和 linux 下wrk 壓測):.NET Core 版本,今天計劃準備壓測一下 .NET 版本,來測試並記錄一下 Taurus.MVC 框架在 .NET 版本的性能,以便後續持續優化改進。 為了方便對比,本文章的電腦環境和測試思路,儘量和... ...
  • .NET WebAPI作為一種構建RESTful服務的強大工具,為開發者提供了便捷的方式來定義、處理HTTP請求並返迴響應。在設計API介面時,正確地接收和解析客戶端發送的數據至關重要。.NET WebAPI提供了一系列特性,如[FromRoute]、[FromQuery]和[FromBody],用 ...
  • 原因:我之所以想做這個項目,是因為在之前查找關於C#/WPF相關資料時,我發現講解圖像濾鏡的資源非常稀缺。此外,我註意到許多現有的開源庫主要基於CPU進行圖像渲染。這種方式在處理大量圖像時,會導致CPU的渲染負擔過重。因此,我將在下文中介紹如何通過GPU渲染來有效實現圖像的各種濾鏡效果。 生成的效果 ...
  • 引言 上一章我們介紹了在xUnit單元測試中用xUnit.DependencyInject來使用依賴註入,上一章我們的Sample.Repository倉儲層有一個批量註入的介面沒有做單元測試,今天用這個示例來演示一下如何用Bogus創建模擬數據 ,和 EFCore 的種子數據生成 Bogus 的優 ...
  • 一、前言 在自己的項目中,涉及到實時心率曲線的繪製,項目上的曲線繪製,一般很難找到能直接用的第三方庫,而且有些還是定製化的功能,所以還是自己繪製比較方便。很多人一聽到自己畫就害怕,感覺很難,今天就分享一個完整的實時心率數據繪製心率曲線圖的例子;之前的博客也分享給DrawingVisual繪製曲線的方 ...
  • 如果你在自定義的 Main 方法中直接使用 App 類並啟動應用程式,但發現 App.xaml 中定義的資源沒有被正確載入,那麼問題可能在於如何正確配置 App.xaml 與你的 App 類的交互。 確保 App.xaml 文件中的 x:Class 屬性正確指向你的 App 類。這樣,當你創建 Ap ...
  • 一:背景 1. 講故事 上個月有個朋友在微信上找到我,說他們的軟體在客戶那邊隔幾天就要崩潰一次,一直都沒有找到原因,讓我幫忙看下怎麼回事,確實工控類的軟體環境複雜難搞,朋友手上有一個崩潰的dump,剛好丟給我來分析一下。 二:WinDbg分析 1. 程式為什麼會崩潰 windbg 有一個厲害之處在於 ...
  • 前言 .NET生態中有許多依賴註入容器。在大多數情況下,微軟提供的內置容器在易用性和性能方面都非常優秀。外加ASP.NET Core預設使用內置容器,使用很方便。 但是筆者在使用中一直有一個頭疼的問題:服務工廠無法提供請求的服務類型相關的信息。這在一般情況下並沒有影響,但是內置容器支持註冊開放泛型服 ...
  • 一、前言 在項目開發過程中,DataGrid是經常使用到的一個數據展示控制項,而通常表格的最後一列是作為操作列存在,比如會有編輯、刪除等功能按鈕。但WPF的原始DataGrid中,預設只支持固定左側列,這跟大家習慣性操作列放最後不符,今天就來介紹一種簡單的方式實現固定右側列。(這裡的實現方式參考的大佬 ...