本文發佈於微信平臺: 程序員面試官前端
超過20w字的「前端面試與進階指南」能夠移步githubnode
對於很多開發者而言,鏈表(linked list)這種數據結構既熟悉又陌生,熟悉是由於它確實是很是基礎的數據結構,陌生的緣由是咱們在業務開發中用到它的概率的確不大.git
在不少狀況下,咱們用數組就能很好的完成工做,並且不會產生太多的差別,那麼鏈表存在的意義是什麼?鏈表相比於數組有什麼優點或者不足嗎?程序員
鏈表是一種常見的基礎數據結構,是一種線性表,可是並不會按線性的順序存儲數據,而是在每個節點裏存到下一個節點的指針(Pointer).github
從本質上來說,鏈表與數組的確有類似之處,他們的相同點是都是線性數據結構,這與樹和圖不一樣,而它們的不一樣之處在於數組是一塊連續的內存,而鏈表能夠不是連續內存,鏈表的節點與節點之間經過指針來聯繫.面試
固然,鏈表也有不一樣的形態,主要分爲三種:單向鏈表、雙向鏈表、循環鏈表.算法
單向鏈表的節點一般由兩個部分構成,一個是節點儲存的值val
,另外一個就是節點的指針next
.小程序
鏈表與數組相似,也能夠進行查找、插入、刪除、讀取等操做,可是因爲鏈表與數組的特性不一樣,致使不一樣操做的複雜度也不一樣.微信小程序
單向鏈表的查找操做一般是這樣的:設計模式
鏈表的查找性能與數組同樣,都是時間複雜度爲O(n).
鏈表與數組最大的不一樣就在於刪除、插入的性能優點,因爲鏈表是非連續的內存,因此不用像數組同樣在插入、刪除操做的時候須要進行大面積的成員位移,好比在a、b節點之間插入一個新節點c,鏈表只須要:
這個插入操做僅僅須要移動一下指針便可,插入、刪除的時間複雜度只有O(1).
鏈表的插入操做以下:
鏈表的刪除操做以下:
鏈表相比之下也有劣勢,那就是讀取操做遠不如數組,數組的讀取操做之因此高效,是由於它是一塊連續內存,數組的讀取能夠經過尋址公式快速定位,而鏈表因爲非連續內存,因此必須經過指針一個一個節點遍歷.
好比,對於一個數組,咱們要讀取第三個成員,咱們僅須要arr[2]
就能快速獲取成員,而鏈表則須要從頭部節點進入,在經過指針進入後續節點才能讀取.
因爲雙向鏈表的存在,單向鏈表的應用場景比較少,由於不少場景雙向鏈表能夠更出色地完成.
可是單向鏈表並不是無用武之地,在如下場景中依然會有單向鏈表的身影:
咱們上文已經提到,單向鏈表的應用場景並很少,而真正在生產環境中被普遍運用的正是雙向鏈表.
雙向鏈表與單向鏈表相比有何特殊之處?
咱們看到雙向鏈表多了一個新的指針prev
指向節點的前一個節點,固然因爲多了一個指針,因此雙向鏈表要更佔內存.
別小看雙向鏈表多了一個前置指針,在不少場景裏因爲多了這個指針,它的效率更高,也更加實用.
好比編輯器的「undo/redo」操做,雙向鏈表就更加適用,因爲擁有先後指針,用戶能夠自由得進行先後操做,若是這個是一個單向鏈表,那麼用戶須要遍歷鏈表這時的時間複雜度是O(n).
真正生產級應用中的編輯器採用的數據結構和設計模式更加複雜,好比Word就是採用Piece Table數據結構加上Command queue模式實現「undo/redo」的,不過這是後話了.
循環鏈表,顧名思義,他就是將單向鏈表的尾部指針指向了鏈表頭節點:
循環鏈表一個應用場景就是操做系統的分時問題,好比有一臺計算機,可是有多個用戶使用,CPU要處理多個用戶的請求極可能會出現搶佔資源的狀況,這個時候計算機會採起分時策略來保證每一個用戶的使用體驗.
每一個用戶均可以當作循環鏈表上的節點,CPU會給每一個節點分配必定的處理時間,在必定的處理時間後進入下一個節點,而後無限循環,這樣能夠保證每一個用戶的體驗,不會出現一個用戶搶佔CPU而致使其餘用戶沒法響應的狀況.
固然,約瑟夫環的問題是單向循環鏈表的表明性應用,感興趣的能夠深刻了解下.
固然若是是雙向鏈表首尾相接呢?這就是雙向循環鏈表.
在Node中有一類場景,沒有查詢,可是卻有大量的插入和刪除,這就是Timer模塊。 幾乎全部的網絡I/O請求,都會提供timeout操做控制socket的超時情況,這裏就會大量使用到setTimeout,而且這些timeout定時器,絕大部分都是用不到的(數據按時正常響應),那麼又會有響應的大量clearTimeout操做,所以node採用了雙向循環鏈表來提升Timer模塊的性能,至於其中的細節就再也不贅述了.
插入!
TimersList <-----> timer1 <-----> timer2 <-----> timer4 <-----> timer3 <-----> ......
1000ms後執行 1050ms後執行 1100ms後執行 1200ms後執行
複製代碼
至此,咱們對鏈表這個數據結構有了必定的認知,因爲其非連續內存的特性致使鏈表很是適用於頻繁插入、刪除的場景,而不見長於讀取場景,這跟數組的特性剛好造成互補,因此如今也能夠回到題目中的問題了,鏈表的特性與數組互補,各有所長,並且鏈表因爲指針的存在能夠造成環形鏈表,在特定場景也很是有用,所以鏈表的存在是頗有必要的。
那麼,如今有一個很是常見的一個面試向的思考題:
咱們平時在用的微信小程序會有最近使用的功能,時間最近的在最上面,按照時間順序日後排,當用過的小程序大於必定數量後,最不經常使用的小程序就不會出現了,你會如何設計這個算法?