JavaScript資料結構之連結串列--介紹

snowLu發表於2019-03-18

前言

hello,我是 snow,因個人原因在專欄中消失了很長時間,想了很多不該想的,做了很多不該做的。偶爾發一些沸點來刷存在感。感謝掘金,相比朋友圈我更喜刷沸點,因為在這裡我能找到共鳴。好了,忽略。

JavaScript資料結構之連結串列--介紹

連結串列

今天來了解下資料結構中的連結串列含義。

1 連結串列和陣列的區別

陣列是需要一塊連續的記憶體空間來儲存,對記憶體的要求比較高。 而連結串列卻相反,它並不需要一塊連續的記憶體空間。連結串列是通過指標將一組零散的記憶體塊串聯在一起。

相比陣列,連結串列是一種稍微複雜一點的資料結構。當然,兩者沒有好壞之分,各有各的優缺點。

陣列可以快速的查詢某個元素,但是在插入和刪除時就要移動大量元素。原因就在於相鄰元素的儲存位置也具有鄰居關係。他們的編號是 0,1,2,3,4,...,n,它們在記憶體中的位置也是緊挨著的,中間沒有空隙,所以就無法快速新增元素。而當刪除後,當中就會留出空隙,自然需要彌補。

所以我們需要這樣一種資料結構: 我們反正也是要讓相鄰元素間留有足夠餘地,那乾脆所有的元素都不要考慮相鄰位置了,哪有空位就到哪裡,只是讓每個元素知道它下一個元素的位置在哪裡。我們可以在第一個元素時,就知道第二個元素的位置在哪;在第二個元素時,再找到第三個元素的位置。這樣,所有的元素都可以遍歷而找到。

因此,為了表示每個資料元素 n 和後繼元素 n+1 之間的邏輯關係,對資料元素 n 來說,除了儲存本身的資訊之外,還需要儲存一個指示其後繼的資訊。我們把儲存元素的域稱之為 資料域,把儲存直接後繼位置的域稱之為 指標域。指標域中儲存的資訊稱做 指標或鏈。這兩部分資訊組成資料元素 n 的儲存映像,稱為 結點

而由 n 個結點鏈結成一個連結串列,稱之為 鏈式儲存結構

2 單連結串列

最簡單最常用的是 單連結串列,此連結串列的每個結點只包含一個指標域。

JavaScript資料結構之連結串列--介紹

如上圖,就是一個簡單的單連結串列示意圖。其中有兩個結點是比較特殊的。他們分別是第一個結點和最後一個節點。我們習慣性地把第一個結點叫做頭結點,把最後一個結點叫做尾結點。頭結點是用來記錄連結串列的基地址。有了它,我們就可以遍歷得到整條連結串列。而尾結點特殊地方它的指標不是指向下一個地方,而是指向一個空地址 NULL,表示這是連結串列上最後一個結點。

我們可以判斷當前結點的 next 是否為空,就知道迴圈是否結束。

就像一家三口並排的手牽手去逛街,爸爸牽著媽媽,媽媽牽著孩子。此時的爸爸就可以當做頭結點,而孩子就是尾結點。

與陣列一樣,連結串列也支援資料的增刪改查。 對比插入和刪除操作,為了保持記憶體資料的連續性,資料需要進行大量的資料搬移工作,所以時間複雜度為 O(n)。而在連結串列中插入和刪除資料,並不需要擔心此事,因為連結串列的儲存空間本身就不是連續的。所以,在連結串列中插入和刪除一個資料是非常快速的。

JavaScript資料結構之連結串列--介紹

對比查詢操作,連結串列就沒有陣列那麼高效了。因為連結串列中的資料並非連續儲存的,所以無法像陣列那樣,根據下標等方法查詢,連結串列需要根據指標依次遍歷,直到找到對應的結點。

3 迴圈連結串列

螢幕前的你都還很年輕,不會覺得日月如梭。可上了點年紀的人,比如我---的父輩們,就會常常感嘆,要是能回到從前該多好,向天再借 500 年可好。也有人說,所謂的成功男人就是 3 歲時不尿褲子,5 歲時能自己吃飯....80 歲能自己吃飯,90 歲能不尿褲子。人生是不是在迴圈呢。

JavaScript資料結構之連結串列--介紹

對於單連結串列,由於每個結點只儲存了向後的指標,到了尾結點就停止了向後鏈的操作。這樣,當中的某一結點就無法找到它的前驅結點了,就如上圖一樣,不能回到從前了。

比如說,我們的市場銷售同學家在北京,需要經常到上海出差。行程就是去兩地之間的各個城市。從北京出發,乘坐高鐵經過多個城市後,再乘坐灰機返回北京。

北京 --> 濟南 --> 蚌埠 --> 南京 --> 蘇州 --> 上海

有一次,他先到南京開會,接下來還要把以上的城市再走一遍。此時有人對他說,不行,你得從上海開始,因為北京是第一站。這時他會對這人說什麼?神經病。他從南京開始,到蘇州,上海,然後再回北京然後去濟南的幾個城市就可以了。顯然這表示是你從當中某一個結點開始遍歷整個連結串列,這是原來的單連結串列結構不能解決的問題。

事實上,把北京和上海連起來,行成一個環就解決了前面所面臨的問題。這就是 迴圈連結串列

將單連結串列中尾結點的指標由空指標指向頭節點,就使整個單連結串列形成一個環,這種頭尾相接的單連結串列就簡稱為迴圈連結串列。

JavaScript資料結構之連結串列--介紹

其實迴圈連結串列和單連結串列的主要差異就在於迴圈的判斷條件上,原來是判斷當前結點的 next 是否為空,現在則是判斷當前結點的 next 是否等於頭結點。

就像一家三口圍成一個圈做遊戲,爸爸牽著媽媽,媽媽牽著孩子,孩子又牽著爸爸。

4 雙向連結串列

今天我們銷售又得出差了,平時都是從北京一路停留到上海的。可是這一次,他得先到上海開會,開完後,然後又得需要例行公事,走訪各個城市,此時他該怎麼辦? 這個時候,那人又出主意了,你可以先飛回北京,然後再一路乘坐火車走遍這幾個城市。

哎,人生中總會避免不了這樣給你出餿主意的人存在。哪有這麼麻煩,他一路再乘坐高鐵回去不就完事了嘛。

就如單連結串列,總是從頭到尾找結點,難道就不可以正反遍歷嗎?

所以這個時候雙向連結串列就登場了。雙向連結串列是在單連結串列的每個結點中,再設定一個指向其前驅結點的指標域。所以在雙向連結串列中的結點都有兩個指標域,一個指向直接後繼,另一個指向直接前驅。

JavaScript資料結構之連結串列--介紹

從上圖中可以看出來,雙向連結串列需要額外的兩個空間來儲存後繼結點和前驅結點的地址。所以,如果儲存同樣多的資料,雙向連結串列要比單連結串列佔用更多的記憶體空間。雖然兩個指標比較浪費儲存空間,但是可以支援雙向遍歷,這樣也帶來了雙向連結串列操作的靈活性。

5 雙向迴圈連結串列

既然單連結串列可以有迴圈連結串列,那麼雙向連結串列當然也可以是迴圈連結串列。你可以停下來想想雙向迴圈連結串列長什麼樣子。

JavaScript資料結構之連結串列--介紹

6 連結串列和陣列的效能對比

JavaScript資料結構之連結串列--介紹
陣列和連結串列的對比,並不能侷限於時間複雜度。而且,在實際開發中,不能僅僅利用複雜度分析就決定使用哪個資料結構來儲存資料。針對不同的型別專案來權衡。當然,在大前端,還是陣列用的最多。

參考書籍

《大話資料結構》

重點

各位大佬,初碰水面,不喜勿噴。如有錯誤,還請指出,謝謝。

下一篇總結下如何寫好連結串列程式碼。

我們下期見。

JavaScript資料結構之連結串列--介紹

相關文章