超清晰 資料結構之佇列

2021-09-29 16:37:52 字數 4613 閱讀 4480

一種先進先出的資料結構(fifo)

佇列可以說是兄弟之間理解起來最為簡單的一種資料結構了記得我在校園第一次接觸的時候,就直接把它和陣列聯絡在一起,因為本質上,陣列就是佇列的一種實現方式,但是我們無法將其等同,很多人會以為陣列是一種陣列型別,其實不對,資料型別資料結構是不一樣的,我們可以這麼想,型別是組成結構的一種組成因子,就像我們搭積木一樣,不同積木的堆積方式,就可以搭出不同的模型(結構),結構通過不同的資料型別來實現,資料結構是一種解決資料問題的存在,而且,陣列和佇列有一點是不同的,陣列完全不是為佇列而生,只是他的一種特性恰好符合佇列的特性,就是可以隊頭出隊,隊尾進隊,不一樣的是,佇列的概念是不允許你插隊的,而陣列本質上是可以的。

生活中隨處可見佇列的影子,佇列的「隊」,跟生活中的「隊」十分的類似,但是前者不允許插隊,後者你懂得,

實際上,佇列一定是順序儲存的嗎?不,不是的,陣列雖然是連續記憶體儲存的,但是佇列卻不是,你可以想想生活中的兩種佇列,一是排隊買門票,這種佇列一般來說,每個人都是站對位置整齊前進,我們可以說這是一種順序的儲存,把土地按塊劃分成記憶體塊,每個人都擁有自己的一小塊,而這些小塊加在一起剛好是連續的一大塊,另一種佇列是什麼呢?網上預約啊,每個人在預約平台預約了自己的位置,都擁有自己的編號,但是大家不需要站在一起,只要用一種標識(預約編號)來識別實際排隊的位置就可以了,其實這就是鍊錶實現佇列的原理。

總結來說:佇列有順序表示鏈式表示兩種儲存方式

佇列也是分很多種的:

1.單向佇列:顧明思議,就是只能允許一端入隊,反向端出隊的佇列

2.雙向佇列:佇列兩端都允許入隊和出隊的操作

3.迴圈佇列:隊頭和隊尾相連線

初始化乙個空佇列時,會向os申請指定長度的記憶體,此時我們假設這個佇列的長度為4,那麼我們可以看做這個佇列共有四個方塊組成:

摘一張百科的圖,我們可以看到一開始頭指標和尾指標都是指向 0 的,每次入隊乙個元素時,尾指標便+1,每次出隊乙個元素時頭指標便加一,我們可以很清楚的看到,在圖c的時候如果在3位置再插入乙個d,那麼後面我們將沒有辦法插入新元素,因為d的後面沒有空間了,但是很明顯0號位明顯是空的,但是按照佇列的要求我們只能從隊尾插入,無法從隊頭插入元素,這就是我們所說的「假溢位」,此時聰明的你可能會發現,將3號位和0號位連在一起形成乙個環,那麼0號位不就在3號位後面了,沒錯,這就是迴圈佇列的想法

佇列的概念我覺得通過上面的說明你應該大致了解了,那麼我們就要說說怎麼實現隊頭出隊,隊尾入隊的要求呢?

3.1 陣列(順序儲存)的實現:

簡單說下陣列的概念,陣列是順序儲存的一種資料結構,有分為索引陣列和關聯陣列(關聯陣列php用hash map實現),擁有索引和值的屬性,索引是從0開始的,其實在計算機領域很多東西都是從0開始算的,因為0也是有意義的。在c語言中,陣列就是陣列,是沒有關聯陣列的概念的,所謂的關聯陣列,在php中,就是索引可以不是陣列的陣列,類似key-vlaue結構,本質上就是利用的雜湊演算法,而且大多數強型別的語言的陣列空間是一次開闢的,什麼意思呢?就是說陣列的長度一開始就是確定的,如果陣列滿了,便需要額外擴充,我喜歡php的原因之一就是php的陣列是不需要開發者自己去開闢空間的。而且會根據使用者自動的擴容。好吧扯遠了。

來吧,是時候翻書的**了,我們來看看迴圈佇列的實現:

①佇列的結構體

#define maxqsize 100   //定義乙個常量,作為佇列的最大長度

typedef struct

sqqueue;

此時,你可能好奇qelemtype是什麼,引百科一段說明來進行解釋:

elemtype(也有的書上稱之為elemtp)是資料結構的書上為了說明問題而用的乙個詞。它是element type(「元素的型別」)的簡化體。 因為資料結構是討論抽象的資料儲存和演算法的,一種結構中元素的型別不一定是整型、字元型、浮點型或者使用者自定義型別,為了不重複說明,使用過程中用「elemtype」代表所有可能的資料型別,簡單明瞭的概括了整體。在演算法中,除特別說明外,規定elemtype的預設是int型。

②佇列的初始化

status initqueue(sqqueue &q)

q.front = q.rear = 0; //頭尾指標指向同乙個位置

return ok; //返回乙個正常標誌,ok一般指1

}

③求佇列的長度

int queuelength(sqqueue q)

如果是普通的佇列,長度則是q.rear-q.front

④入隊,將元素填入隊尾,隊尾指標+1

status enqueue(sqqueue &q,qelemtype e)

⑤出隊,隊頭元素出隊,隊頭指標+1

status dequeue(sqqueue &q,qelemtype &e)

需要注意的是,迴圈佇列對索引的操作都是取餘的方式,以及status也是一種泛型別指定,它可以是int 也可以是string,偽**的使用十分的頻繁,讀者不需要過多研究

3.2 鏈隊(鏈式儲存)的實現:

這篇文章

其實鏈式儲存方式是一種對記憶體使用比較良好的儲存方式,它不需要依賴連續的儲存空間,也不需要想順序儲存那樣需要指定乙個最大的儲存長度,它的長度是可變的,動態的,這裡我不得不提以下php陣列的儲存方式,它就是一種鏈式儲存,雖然它支援for迴圈和foreach遍歷兩種方式,實際上它的for迴圈所使用的索引是php所生成的乙個對映表,所以在實際情況下,php的for比foreach的效率更加低,乙個原因是因為存在$i的比較,另乙個就是需要用到對映表,而foreach則是直接遍歷鍊錶,因為鏈式儲存可以隨意的擴充套件長度,因此跟強型別的一次性開闢空間的做法相比更有優勢

接下來我們直接來看c語言的鏈隊的偽**實現:

①鏈式的儲存結構

typedef struct qnode

qnode,*queueptr;

typedef struct

linkqueue;

我們知道 typedef 是 c 語言的乙個取別名的關鍵字,這裡大家對結構體不熟悉的話可能會有疑問,這兩個結構體的區別是什麼?

首先,我們不去關心結構體裡面的內容,因為裡面就是真正要儲存的東西,你想放什麼都可以,主要是對結構體的定義宣告,我這裡想提一下,

為什麼第乙個結構體和第二個結構 struct 後面乙個有帶名字乙個沒帶呢?

struct *** 這個***實際上是這個結構的識別符號,*** 單獨使用是沒有意義的,但是 struct *** 是有意義的,它是一種自定義的結構型別 (跟 int float 這種預定義型別是同一階級的),因此我們如果想宣告某個變數的內部結構是我們定義的這種,只需要這樣宣告:struct *** a,但是這樣聲明顯很累贅,因此一般來說我們會使用 typedef 來宣告別名,例如第乙個 qnode === struct qnode, 此時,struct qnode a 就可以簡寫成  qnode a

② 鏈隊的初始化

status initqueue(linkqueue &q)

③ 鏈隊的入隊

status enqueue(linkqueue &q, qelemtype e)

④ 鏈隊的出隊

status dequeue(linkqueue &q, qelemtype &e)

⑤ 鏈隊的隊頭元素

selemtype gethead(linkqueue q)

}

過了一段時間重看這段偽**,是有幾個地方比較模糊的,比如說為什麼需要尾指標呢?

我嘗試思考,如果只有頭指標,沒有尾指標會出現什麼後果,例如在入隊的時候,我們確實可以根據頭指標找到隊頭元素,而且q.front->next == 隊頭 ,q.front->next->next == 隊頭第二個人,我們確實可以根據這樣的遞迴來找到需要的元素,但是入隊怎麼辦呢,難道我每次入隊都要從頭遍歷一下來找到最後乙個元素,然後才在最後乙個元素後面進行入隊操作,顯然就是浪費時間。所以多加個尾指標是有意義的。

結點是乙個開闢的空間,裡面有儲存所需要儲存的東西,我們每次入隊乙個元素,都會增加乙個口袋,裡面放著需要的東西,出隊則直接操作頭指標,入隊則直接操作尾指標,我們通過這個 ->next 的方式指向,將這個原本毫無關係的口袋拿一條有方向的繩子綁了起來,形成乙個具有多個口袋的繩子。

如果我們用這種實際的例子去讓這種思想不再抽象化,就會較容易的理解程式為什麼要這麼設計,現實例子模擬的方法適用於很多生活中的演算法,畢竟程式是因生活而生。

超清晰 資料結構之線性表

鳥哥說,堅持學習基礎才能有出人頭地的一天。不能只專注於練武功了,內功也得練。本篇文章是講資料結構的第一篇,跟著書好好再過一篇基礎。一 線性表 線性表是n個資料特性相同的元素的組成有限序列,是最基本且常用的一種線性結構 線性表,棧,佇列,串和陣列都是線性結構 同時也是其他資料結構的基礎。對於非空的線性...

資料結構之佇列

八 佇列 鏈式佇列 鍊錶實現 隊尾 rear 隊首 front 靜態佇列 陣列實現 必須是迴圈佇列 需要幾個引數來確定,各引數含義 1 佇列初始化 front和rear值都是0 2 佇列非空 front代表佇列第乙個元素 rear代表佇列最後乙個元素的 下乙個元素 3 佇列空 front和rear相...

資料結構之佇列

與棧相反,佇列是一種先進先出的線性表,它只允許在表的一端進行,而在另一端刪除元 素。在佇列中,允許插入的一端叫做隊尾,允許刪除的一端則稱為隊頭。1 鏈佇列 佇列的鏈式表示和實現 用鍊錶表示的佇列簡稱為鏈佇列,乙個鏈佇列顯然需要兩個分別指示對頭和隊尾的指標 分別稱為頭指 針和尾指標 才能唯一確定。這裡...