算法與數據結構(二) 棧與隊列的線性和鏈式表示(Swift版)


數據結構中的棧與隊列還是經常使用的,棧與隊列其實就是線性表的一種應用。因為線性隊列分為順序存儲和鏈式存儲,所以棧可以分為鏈棧和順序棧,隊列也可分為順序隊列和鏈隊列。本篇博客其實就是《數據結構之線性表的順序存儲於鏈式存儲(Swift面向對象版)》這篇博客的應用。本篇博客會分別給出隊列的順序和鏈式存儲,以及棧的順序和鏈式存儲。

說到棧和隊列這兩種數據結構,理解起來應該不難。隊列就是進行排隊的數據結構,一個隊列肯定是線性結構了,之所以稱之為隊列,是因為有着先入先出(FIFO ----first in first out)的特性。就像你去銀行辦業務排隊時,你先排的隊當然是你先辦理業務,那些后排隊的要在你后邊辦業務。而棧就與隊列相反了,具有先入后出(FILO -- first in last out)的特性。在現實生活中手槍的子彈夾就是棧的結構,最先進去的子彈會最后才射出。當然在我們做iOS開發時,會經常使用到導航棧,而導航棧中存儲的就是你之前Push進的頁面,也是先入后出的特性。關於棧和隊列,下方會給出詳細的介紹。

 

一、棧與隊列的綜述

棧與隊列毫無疑問都是線性結構的,分別適用於不同的場景。博客的本部分會給出棧與隊列的總體介紹,然后分別給出其實現方案。當然下方在棧與隊列的實現中,我們依然采用“面向接口”編程的思想,會先定義棧與隊列的相關接口,然后給出棧與隊列的不同實現方案。

下方這個就是一個典型的隊列的結構。需要加入隊列中的元素是往隊尾添加的,而需要出隊的元素從隊頭出。這樣出隊列的順序與進入隊列的順序是一致的。這也就是隊列的特性,先入先出。之前我們在聊GCD的中的隊列的時候也同樣適應這個特性。在GCD中無論是串行隊列還是並行隊列,其都遵循隊列“先入先出”的規則

  

上面我們簡單的聊了一下隊列,接下來我們來簡單的聊一下棧。在博客的開頭也提到了,彈構就是棧結構。彈夾中的子彈就是棧中的元素,遵循着先入后出的原則。下方這個示例圖就是一個典型的棧型結構。在棧中有一個指針Top,永遠指向棧頂元素,如果棧為空,那么Top就為nil。在棧結構中無論是入棧還是出棧,都是操作棧頂元素。所以入棧順序與出棧順序是相反的。

  

 

二、線性隊列和鏈式隊列的實現

聊完原理,接下來我們就要來進行代碼實現了。下方將會給出具體的隊列的實現代碼,並給出相應的測試用例。當然我們依然是采用面向對象的思想來實現的隊列的。在看具體代碼之前,我們先來看一下我們本篇博客所涉及Demo的具體目錄。最上面的框是兩個協議,StackProtocol中聲明了棧結構所涉及的操作,所有棧都要遵循該協議。QueueProtocol聲明了隊列中所涉及的所有操作,隊列的實現都要遵循該隊列協議。

  

這樣做的好處就是所有類型的棧可以共用棧的測試用例,而隊列也是如此。下方就是我們測試用例的調用方式,需要測棧時,就給棧的測試用例傳入相應棧的對象,隊列也是一樣。也就能明顯看出面向接口編程的好處。

  

 

1.隊列協議:QueueType

在具體實現隊列之前,我們先定義隊列的接口。此處我們要定義的就是QueueType協議,在QueueType中聲明的是隊列的全部操作。無論是棧隊列,還是順序隊列都必須要遵循該接口,而在外部聲明隊列類型時,我們一般會使用QueueType來聲明隊列類型。類型為QueueType的隊列可以被賦值為遵循QueueType協議的類的任何對象。下方就是QueueType協議中的內容。

從下方的代碼段中,我們顯然可以看出QueueType協議就是賦值為我們的隊列實現定制大綱的。有了這個大綱,具體隊列的實現要按照下方這個大綱來。至於隊列的具體實現細節,QueueType協議並不關心,QueueType關系的是隊列對外的使用方式。

  

 

2.順序隊列

接下來我們就依據上述的隊列的協議,給出順序隊列的具體實現代碼。順序隊列我們就以Swift中的數組類型來代替了。enQueue--入隊列所對應的操作就是往數組的尾部添加數據,而deQeueu--出隊列操作就是將數組第一個元素進行移除並返回移除的值即可。下方就是入隊列和出隊列的操作,如下所示。隊列的其他操作在此就不做過多贅述了,請參考github上分享的代碼。

  

 

3.鏈式隊列

鏈式隊列其實就是鏈表的一種使用方式。鏈式隊列就是講隊列元素以鏈表的形式進行存儲,並且規定只能從鏈表的尾部添加元素,從鏈表的頭部移除元素。關於鏈表的各種操作請參考上篇博客《數據結構之線性表的順序存儲於鏈式存儲(Swift面向對象版)》中介紹的內容。該部分就是鏈表在隊列中的應用。與上面的內容類似,下方是鏈式隊列的核心操作,下方截圖中的代碼段是鏈式隊列中出隊列和入隊列的操作了。如下所示:

  

 

4.隊列的測試用例

上面我們分別實現了鏈式隊列和順序隊列,接下來我們將會對上面這兩種隊列進行測試。由於上面這兩種隊列有種統一的對外調用接口,也就是這兩種隊列都遵循QeueuType這個隊列協議。所以在測試隊列時我們可以使用同一個測試用例,這也就是“面向接口編程”的好處。當我們再引入其他隊列的具體實現方案時,只需要將新引入的隊列解決方案遵循我們之前定義的QueueType接口即可,我們的測試用例仍然好用。從這一點我們就能看出“面向接口”編程的可維護性和高擴展性

下方就是我們隊列的測試用例, 函數的參數是QueueType的類型。也就是只要遵循QueueType協議的所有類的對象都可以作為該函數的參數。最下方的兩行代碼是該函數的調用方式。第一個傳入的是順序隊列的對象,所有測試的就是順序隊列相關代碼。而第二個傳入的是棧隊列的對象,那么測試的就是棧隊列的相關代碼。

  

下方就是測試用例的運行結果,先將a, b出隊列,然后將x,y,x如隊列。

  

 

三、棧的順序存儲與鏈式存儲

上面已經聊完隊列的相關內容了,接下來我們在按照上面的方式來聊一下棧的內容。再重復一遍棧的規則:先入后出。先入后出是棧的特定,當然棧也屬於邏輯結構中線性結構,基於線性結構的特定,所以棧也是有這鏈式和順序存儲的結構的。下方將會給出棧的這兩種實現。在具體實現不同類型的棧時,我們還是依照“面向接口”編程的思想,先定義出棧的協議StackTypeStackType協議中定義了棧的所有相關操作,棧的具體實現要遵循該協議。這樣做的好處就不做過多贅述了。

 

1.棧協議StackType的定義

首先我們還是來定義棧的接口StackType。下方截圖中的代碼段就是我們定義好的棧的接口,也就是Swift語言中的協議。從下方協議中我們不難看出,只聲明了方法,而沒有具體實現。具體實現我們放在不同類型的棧中去做。因為具體實現的棧要對外統一調用接口,所以必須遵循StackType協議。

  

 

2.棧的順序存儲實現

定義完棧的協議后,我們就該遵循該協議給出具體的實現了,接下來我們要給出順序棧的實現方式。此處為了簡單期間,我們就使用Swift的數組(Array)變量來實現。當然入棧和出棧操作都是借助Array自帶的操作來實現的。下方截圖中就是順序棧中入棧(push)和出棧(pop)的操作。因為我們借助了Array本身的操作,也就是Array為我們做了許多事情,所以實現起來就比較簡單了。

下方就是順數棧的主要操作,push()方法就是將該函數的參數進行入棧操作。其實就是調用的Array的append()方法,將該參數存入到數組的最后一個位置。而pop()方法負責移除並返回棧頂元素,此處我們借用了Swift語言中的Array的removeLast()方法,來移除數組的最后一個元素,然后將這個元素進行返回。從上述操作步驟來看,棧的特點就是對棧頂元素進行操作。

  

 

3. 棧的鏈式存儲實現

上面的順序存儲,我們使用的是順序存儲,借助了Array來實現的,所以操作起來比較簡單。棧的鏈式存儲操作起來會相對麻煩一些,不過這些操作在上篇博客中已經進行了詳細的介紹,所以對本篇博客來說並非難事。

下方這段代碼就是鏈式存儲的棧的核心操作。push()方法賦值的就是往棧中添加新的元素,首先會創建一個新的結點,然后添加到棧頂元素中。棧頂元素我們用top指針來進行標記。入棧后我們還要將棧的長度進行加一操作。然后就是pop()出棧操作了,首先會判斷棧是否為空,如果不為空就返回棧頂元素,並將棧頂元素進行移除。移除棧頂元素后需要將top指針指向新的棧頂並將棧中的元素進行減一操作。具體代碼如下所示。

  

 

4.棧的測試用例

因為上述我們棧的實現都遵循了我們事先定義好的StackType協議,所以上述兩種類型的棧可以使用一個測試用例。這一點與上述隊列的測試用例是一致的,接下來我們將要來測試我們的棧。下方testStack()函數就是我們棧的測試函數,函數的參數類型是StackType。所有遵循StackType協議的棧的對象都可以作為該函數的參數。最后兩行是函數的調用方式,第一個傳入的是順序棧的對象,第二個傳入的參數是鏈棧的對象。

  

由上面這段代碼可看出,該測試用例是適用於StackType系列的棧。下方就是測試用例的輸出結果,輸出結果還算是直觀形象,所以在此就不做過多的贅述了。

  

 

至此呢,我們的棧與隊列的順序存儲和鏈式存儲就聊完了。當然,本篇博客只是拿出了Demo的一部分來講的,更完整的示例還是看github上分享的Demo吧。

github分享地址:https://github.com/lizelu/DataStruct-Swift/tree/master/StackAndQueue

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM