編程小白真香現場!20張圖帶你揭開「隊列」的迷霧,讓你一目瞭然

隊列的概念

首先咱們聯想一下鏈表,在單鏈表中,咱們只能對他的鏈表表尾進行插入,對鏈表的表頭進行結點的刪除,這樣強限制性的鏈表,就是咱們所說的隊列。編程

也就是說,隊列(queue)是限定在表的一端進行插入,表的另外一端進行刪除的數據結構。微信

以下圖所示,假如你去買票排隊,每一列隊伍都有一個隊尾和對頭,先來的先買票,後來的後買,買好的就從對頭出去,新來買票的就須要從隊尾繼續排隊。數據結構

一般,稱進數據的一端爲 隊尾,出數據的一端爲 隊頭,數據元素進隊列的過程稱爲 入隊,出隊列的過程稱爲 出隊函數

ID:技術讓夢想更偉大學習

做者:李肖遙優化

咱們能夠總結以下:設計

隊列是一個線性的數據結構,而且這個數據結構只容許在一端進行插入,另外一端進行刪除,禁止直接訪問除這兩端之外的一切數據,且隊列是一個先進先出的數據結構。3d

如上圖,隊列就像一個兩端相通的水管,只容許一端插入,另外一端取出,取出的球就不在水管裏面了,而先放入管中的球就會先從管中拿出。指針

隊列存儲結構的實現有如下兩種方式:調試

(1)順序隊列:在順序表的基礎上實現的隊列結構

(2)鏈隊列:在鏈表的基礎上實現的隊列結構

二者的區別僅是順序表和鏈表的區別,即在實際的物理空間中,數據集中存儲的隊列是順序隊列,分散存儲的隊列是鏈隊列。

隊列的結點設計與初始化

隊列只有鏈式的設計方法,其自己分爲多種隊列,如順序隊列循環隊列,還有衍生的優先隊列等等,咱們以順序隊列的設計爲例。

首先是隊列的結點設計,咱們能夠設計出兩個結構體,一個結構體Node表示結點,其中包含有一個data域和next指針,如圖所示:

其中data表示數據,其能夠是簡單的類型,也能夠是複雜的結構體。

next指針表示,下一個的指針,其指向下一個結點,經過next指針將各個結點連接。

而後咱們再添加一個結構體,其包括了兩個分別永遠指向隊列的隊尾和隊頭的指針,看到這裏是否是以爲和棧很像?

咱們主要的操做只對這兩個指針進行操做,如圖所示:

其結構體設計的代碼能夠表示爲:

對於初始化須要初始化兩個類型,一個是初始化結點,一個是初始化隊列。

咱們看到代碼中的描述,初始化隊列有些不一樣,當初始化隊列的時候,須要將頭尾兩個結點指向的內容通通置爲空,表示是一個空隊列,兩個建立的函數代碼能夠表示爲:

判斷隊列是否爲空

這是一個既簡單也很要緊的操做,判斷隊列是否爲空直接就是判斷隊列頭指針是不是空值便可,判斷隊列是否爲空是比較經常使用的操做,切勿忘記。

其代碼能夠表示爲:

或者直接利用返回值進行更簡單的判斷也能夠,代碼以下:

入隊操做

入隊操做變化以下圖:

進行入隊(push)操做的時候,一樣的,咱們首先須要特判隊列是否爲空,若是隊列爲空的話,須要將頭指針和尾指針一同指向第一個結點,代碼以下

如圖所示:

當若是隊列不爲空的時候,這時咱們只須要將尾結點向後移動,經過不斷移動next指針指向新的結點構成隊列便可。如圖所示:

其代碼能夠表示爲:

出隊操做

出隊操做變化以下圖:

出隊(pop)操做,是指在隊列不爲空的狀況下進行的一個判斷,固然咱們在此也必定要進行隊列判空的操做,你懂的。

如圖,若是隊列只有一個元素了,也就是說頭尾指針均指向了同一個結點,那麼咱們直接將頭尾兩指針制空NULL,並釋放這一個結點便可,以下圖所示:

當隊列含有以上個元素時,咱們須要將隊列的頭指針指向頭指針當前指向的下一個元素,並釋放掉當前元素便可,以下圖所示

其代碼能夠表示爲:

打印隊列元素(遍歷)

打印隊列的所有元素能夠幫助咱們調試,看到隊列中具體的數據,在隊列不爲空的狀況下,經過結點的next指向依次遍歷並輸出元素既可。

其代碼能夠表示爲

遍歷操做還有不少別的表示方法,好比說進行計算隊列中含有多少元素,代碼以下:

順序隊列的假溢出

什麼是假溢出?咱們可能會有疑問,溢出還有假的!

這裏咱們也須要考慮到順序隊列有什麼缺點,對於順序隊列而言,其存在已經足夠解決大多時候的設計問題了,可是其依舊存在一些缺陷和不足。

從上面的解析中咱們看到,入隊和出隊操做均是直接在其後面進行結點的連接和刪除,這種操做會形成其使用空間不斷向出隊的那一邊偏移,產生假溢出。

咱們來打打一個比方,先看看下面的圖:

示例順序隊列

上圖所示,有一個順序隊列,這個隊列的大小爲5,其已經包含了四個元素data1,data2,data3,data4。

接着,咱們對這個隊列進行出隊操做,出隊2個元素,隊列就變成了這個樣子,以下圖所示:

從圖上看到彷佛沒有什麼問題,可是當咱們接着再進行入隊操做,好比咱們入隊2個元素,分別是data5和data6。

此時咱們已經發現問題了,尾指針移動到咱們能夠進行隊列操做的範圍以外去了,有沒有發現?

這種現象咱們稱呼做爲隊列用的存儲區尚未滿,但隊列卻發生了溢出,咱們把這種現象稱爲假溢出。以下圖所示:

出隊產生假溢出

那麼咱們有什麼辦法解決這個問題呢?這就要涉及到循環隊列的性質了!

循環隊列的概念

可能這個時候會產生一個疑問,咱們學習的隊列不是使用鏈表實現的動態隊列麼?

沒有空間的時候會開闢空間,這難道還會產生假溢出麼?

的確,當進行動態建立隊列的時候,也只不過是向後繼續不斷的申請內存空間;

即便前面出隊操做釋放掉了前面的空間,可是指針依舊會向後進行移動,直到達到系統預留給程序的內存上界被強行終止;

這對於極爲頻繁的隊列操做和程序而言是致命的,這時候,就須要對咱們的隊列進行優化,使用更爲優秀的結構——循環隊列

循環隊列就是將隊列存儲空間的最後一個位置轉而繞到第一個位置,造成邏輯上的環狀空間,以此來供隊列循環使用,以下圖。

循環隊列就是給定咱們隊列的大小範圍,在原有隊列的基礎上,只要隊列的後方滿了,就從這個隊列的前面開始進行插入,以達到重複利用空間的效果;

因爲循環隊列的設計思惟更像一個環,所以常使用一個環圖來表示,但咱們須要注意,實際上循環隊列不是一個真正的環,它依舊是單線性的。

循環隊列的結構設計

因爲循環對列給定了數據範圍的大小,因此不須要使用鏈式的動態建立方法了。

由於若是使用鏈式存儲,會沒法肯定什麼時候再回到隊頭進行插入操做,因此咱們採用模擬的方法,如圖所示:

其中,data表示一個數據域,int爲類型,其能夠修改成任意自定義的類型,好比說簡單的char,float類型等等,也能夠是複雜的結構體類型。

(1)maxsize表示循環隊列的最大容納量,其表示隊列的所有可操做空間。

(2)rear表明尾指針,入隊時移動。

(3)front表明頭指針,出隊時移動。

其代碼能夠表示爲:

循環隊列的初始化

循環隊列的初始化核心就在於申請空間,而且將front指針和rear指針內容賦值爲0,即指向第0個元素便可,這裏要注意第 0個元素內容爲空,以下圖所示:

其代碼能夠表示爲:

入隊操做

入隊操做同順序隊列的方法,直接將rear向後移動便可。

可是要注意判斷,若是rear達到了隊列的空間上線,將要從頭繼續開始移動。

這裏推薦使用餘數法,即不管如何求餘都是在這片空間內進行操做,防止一次錯誤執行就直接總體崩潰,並且也相對而言更爲簡潔,不推薦使用if語句,這樣顯得比較累贅。

入隊操做

注意進行加一移動位置操做的時候,不能直接q->rear++這樣的操做,這樣計算機判斷優先級會產生讓本身意想不到的後果。

此外這裏還須要進行一次是否隊列已滿的判斷,當咱們rear指針的下一個位置就是front的位置的時候,即改循環隊列已滿。

如圖:

出隊操做

若是順序隊列的出隊操做,直接將front進行後移一位便可。

這裏上面不少地方都提過了,有一個須要留意的地方,即隊列是否爲空,當隊列爲空的時候是沒法進行出隊操做的。

其代碼能夠表示爲:

遍歷操做

遍歷操做須要藉助一個臨時變量儲存位置front的位置信息,利用i逐步向後移動,直到i到達了rear的位置便可宣告遍歷的結束。

關於隊列的總結

請牢記這句話:隊列是一個先進先出的數據結構。

今天隊列基礎就講到這裏,下一期,咱們再見!

另外若是你想更好的提高你的編程能力,好好學習C/C++編程知識的話!那麼你很幸運~

C語言C++編程學習交流圈子,QQ羣757874045點擊進入】微信公衆號:C語言編程學習基地

分享(源碼、項目實戰視頻、項目筆記,基礎入門教程)

歡迎轉行和學習編程的夥伴,利用更多的資料學習成長比本身琢磨更快哦!

編程學習書籍

編程學習視頻

相關文章
相關標籤/搜索