《數(shù)據(jù)結(jié)構(gòu)與算法之美》-鏈表

數(shù)組和鏈表

數(shù)據(jù)是使用連續(xù)的內(nèi)存空間存儲數(shù)據(jù)。

鏈表是使用不連續(xù)的內(nèi)存空間存儲數(shù)據(jù)。

常見鏈表鏈表結(jié)構(gòu)

  • 單鏈表

  • 循環(huán)鏈表

  • 雙向鏈表

基本概念

結(jié)點

分配用來存儲數(shù)據(jù)的每個不連續(xù)的內(nèi)存塊就是結(jié)點。

后繼指針

既然內(nèi)存塊不是連續(xù)的,那么這些值是如何關(guān)聯(lián)起來的。這時候就會通過后繼指針來串聯(lián)這些結(jié)點。每個結(jié)點除了存儲數(shù)據(jù)外,還要存儲下一個結(jié)點的內(nèi)存地址,就是通過后繼指針next指向的。

頭結(jié)點

頭結(jié)點就是鏈表起始結(jié)點,用于存儲鏈表的基地址。

頭結(jié)點一般不存儲數(shù)據(jù)。

指向頭結(jié)點的指針成為頭指針。

尾結(jié)點

就是最后一個結(jié)點,該結(jié)點指針區(qū)域不指向任何結(jié)點,而是指向一個空地址NULL。

單鏈表

單鏈表從頭結(jié)點開始到尾結(jié)點結(jié)束的鏈表。

其插入和刪除的時間復(fù)雜度為O(1),查找的時間復(fù)雜度為O(n)

對應(yīng)的,數(shù)據(jù)插入和刪除的時間復(fù)雜度為O(n),查找的時間復(fù)雜度為O(1)

循環(huán)鏈表

區(qū)別于單鏈表,循環(huán)鏈表就是將尾結(jié)點的指針指向了頭結(jié)點,形成一個閉環(huán),即為循環(huán)鏈表。

循環(huán)鏈表的優(yōu)點就是從鏈尾到鏈頭比較方便。

雙向鏈表

顧名思義,每個結(jié)點不僅有指向下一個結(jié)點,同時還有了指向上一個結(jié)點。

顯然,相比之前的鏈表結(jié)構(gòu),該鏈表結(jié)構(gòu)增加了空間消耗,那么必然,在時間上會得到補償,典型的空間換時間。

下面我們來看看雙向鏈表是如何實現(xiàn)空間換時間的。

我們還是說回普通鏈表的插入和刪除操作。

刪除操作

更加準(zhǔn)確的說刪除操作會有兩種情況

  • 刪除結(jié)點中“值等于某個給定值”的結(jié)點

  • 刪除給定指針指向的結(jié)點

對于第一種情況,如果只是刪除,那么時間復(fù)雜度就是O(1)。但是在此之前,我們需要找到這個節(jié)點,鑒于鏈表內(nèi)存結(jié)構(gòu)的不連續(xù)性,需要一一遍歷才能找到這個結(jié)點,所以時間復(fù)雜度是O(n),最終時間復(fù)雜度也是O(n)。

對于第二種情況,和第一種情況類似,遍歷找到這個接口并刪除,時間復(fù)雜度也是O(n)。

上面說的是單鏈表,如果這時候是雙鏈表結(jié)構(gòu),情況就會不太一樣。

第一種情況,依然無法避免,因為鏈表內(nèi)存結(jié)構(gòu)不連續(xù)的事實無法改變。

第二種情況,具體的操作是找到指定的結(jié)點,然后將該結(jié)點的上一個結(jié)點的后繼指針指向該結(jié)點的下一個結(jié)點。但是在雙線鏈表就不一樣了,找到當(dāng)前節(jié)點就可以無縫平滑的找到其上一個結(jié)點,而不需要傻乎乎的從頭結(jié)點開始遍歷一直找。所以這時候的時間復(fù)雜度從O(n)變?yōu)榱薕(1)。這就是典型的空間換時間。

空間換時間和時間換空間

針對不同的場景,我們需要在時間和空間之前做一個權(quán)衡。

比如緩存就是一個典型的空間換時間,我們希望接口的響應(yīng)速度更快更及時,所以我們一次性將數(shù)據(jù)加載到緩存即內(nèi)存,這樣訪問的速度變快了,但是在空間層面上,我們占用了更多的內(nèi)存資源。

分頁請求等這些就是時間換空間,我們每次只使用有限的空間存儲返回結(jié)果,但是需要分多次請求返回(可能不是十分恰當(dāng),意在表明,當(dāng)空間資源稀缺貨有限時,可以通過延長時間來彌補)

數(shù)組和鏈表的比較

數(shù)組是固定大小的,如果要擴容,需要重新申請一個更大的內(nèi)存空間并拷貝原來的數(shù)組內(nèi)容。

鏈表是天然支持動態(tài)擴容的,能夠很好的利用碎片的內(nèi)存空間。

如果你的代碼對內(nèi)存使用非??量?,建議使用數(shù)組。因為鏈表除了保存數(shù)據(jù)的內(nèi)存空間,還需要內(nèi)存空間用來存儲下一個結(jié)點的內(nèi)存地址,所以空間占用會翻倍。

鏈表的插入和刪除會帶來內(nèi)存的申請和釋放,容易造成內(nèi)存碎片,對于Java語言來說,會導(dǎo)致頻繁的GC(垃圾回收)。

如何基于鏈表實現(xiàn)LRU緩存淘汰算法

LRU(Least Recently used)最近最少使用算法是緩存淘汰的算法之一。

意思就是最近時間內(nèi),使用次數(shù)最少的緩存內(nèi)容就優(yōu)先刪除。

實現(xiàn)思路

使用一個鏈表存儲緩存的數(shù)據(jù),從頭至尾,越靠近頭結(jié)點表示越是最近訪問的數(shù)據(jù),越靠近尾結(jié)點表示越是久遠(yuǎn)訪問過的數(shù)據(jù)。

如果訪問的某個數(shù)據(jù)已經(jīng)被緩存在這個鏈表中,我們通過遍歷找到這個結(jié)點后就刪除,然后將這個結(jié)點插入到頭結(jié)點。

如果這個數(shù)據(jù)沒有在鏈表中,這時候繼續(xù)判斷,如果緩存沒有滿,則將此結(jié)點插入到頭結(jié)點。如果緩存已滿,則刪除尾結(jié)點,并將新數(shù)據(jù)插入頭結(jié)點。

使用鏈表實現(xiàn)這個問題,正是利用鏈表插入和刪除方便的特性,同時頭尾指針的順序也正好可能映射緩存被訪問的熱度。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 城市沒有夜晚,凌晨快一點才到的家,隨便一收拾已經(jīng)兩點多,困得實在不行靠著沙發(fā)睡著了。早晨在淅淅瀝瀝雨聲中醒來,...
    方小姐的小生活閱讀 233評論 0 0
  • 是個法兒 先二手電腦 寫著寫著 就不一樣了 一念無明 進(jìn)入抑郁 一念天明 即可逃離 抑郁是心病 得用心藥醫(yī) 病重再...
    鐵娃嬌涯閱讀 349評論 1 8
  • 1、 周一絕對是上班族最頭疼的日子,從坐下來的那一刻起,就不得閑,各項工作,各路電話,還有各種碰頭會,今晚的會議過...
    夢竹草閱讀 1,549評論 2 5
  • 為什么一家上市公司,能夠在半年內(nèi)的時間內(nèi),接連發(fā)生兩起虐童事件,而且都是性質(zhì)極為嚴(yán)重、及其惡劣的事件。 在第一時間...
    石頭聊家庭教育閱讀 625評論 2 5
  • 博客原文 日志滿天飛總是不好的,代碼是程序員的藝術(shù)品,日志也是其中一部分。 寫在前面的話 程序員在程序中打印日志的...
    rabbitGYK閱讀 2,378評論 1 10

友情鏈接更多精彩內(nèi)容