數據結構與算法-鏈表(下)

承接上文,解決普通鏈表查找的問題。首先分析問題的瓶頸,對於查找,天然是從頭開始順序查找到尾部,那麼怎麼才能更快查找到目標元素呢?將鏈表中的元素排序能夠加速查找過程,但仍須要順序查找。所以,鏈表最好容許跳過某些節點,以免順序處理。基於以上思路,提出跳躍鏈表的概念。跳躍鏈表是有序鏈表的一個有趣變種,能夠進行非順序查找,算法複雜度是O(lgn),相比於O(n)降低了一個量級。
前端

首先使用天然語言概述跳躍鏈表。假設咱們已有N個元素組成的有序單向鏈表,從中取出n1個元素組成新的單向鏈表,咱們稱之爲二層鏈表,而後從二層鏈表中繼續取出n2個元素組成三層鏈表,最後以此循環,能夠有m層鏈表,m隨意定義。這就是跳躍鏈表。就像蓋高樓同樣,普通鏈表只有一層,查找某個元素只能從頭至尾,可是若是咱們組織多層鏈表,就能夠從高層到低層逐層查找,在這個過程當中能夠跳過不少元素,達到快速查找的目的。算法

下面是數學定義:bash

位於2^(k-1)*i的節點指向位於2^(k-1)*(i+1)的節點,約束條件:i>=1,1<=k<=lgn,lgn向下取整複製代碼

跳躍鏈表:數據結構

這意味着第2個節點指向前面距離2個單位的節點,第4個節點指向前面距離4個單位的節點,以此類推。爲此,要在鏈表中包含不一樣數目的指針:半數節點只有一個指針,1/4節點有兩個指針,1/8節點有3個指針,以此類推。post

概述下查找的邏輯:測試

假設查找元素el,應該從最高層上的指針開始,找到該元素就成功的結束查找。若是到達該鏈表的末尾,或者遇到大於元素el的某個元素key,就從包含key的那個節點的前一個節點從新開始查找,可是此次查找是從比前面低一層的指針開始的。直到找到el,或者沿着第一層的指針到達鏈表的末尾,或者找到一個大於el的元素,查找纔會中止。spa

舉個例子,好比在上面的跳躍鏈表中查找5,那麼首先嚐試的是第四層,這一層第一個節點是8,查找不成功,接着回退8節點的上一個節點。找到root指針,查找root第3層,首先找到4,而後找到8,查找不成功,回退8節點的上一個節點。找到節點4,查找節點4第2層,首先找到6,查找不成功,回退6節點的上一個節點。找到4節點,查找節點4第1層,首先找到5,查找成功。
設計

動態圖以下:指針

跳躍鏈表的查找複雜度只有O(lgn),這是很不錯的效率了,可是鏈表的設計使得插入和刪除效率很低。爲了插入一個新的元素,必須從新構建新節點以後的全部節點,這固然是不能容忍的。爲了保持跳躍鏈表在查找方面的優勢,同時避免在插入和刪除節點時從新構造鏈表,咱們能夠放棄對不一樣層上節點的位置要求,僅保留不一樣層上節點的數目要求便可。經過計算能夠知道不一樣層級之間節點數目比例以下:code

假設層級爲n,那麼從n層到1層的節點數目比例爲:2^0 : 2^1 : ... : 2^(n-1)

好比上面跳躍鏈表是4層,那麼比例就是1:2:4:8。這裏有一點須要注意,由於咱們放棄了位置要求,那麼鏈表層級是能夠隨意指定的,通常是4層,即便節點數目有10000個,只要層級之間節點數目比例保持不變,就不會影響查找效率。

使用上述方法,添加和刪除的算法複雜度也保持在O(lgn),這是由於添加和刪除也依賴查找,跳躍鏈表是順序表,添加或刪除時首先依賴查找肯定節點位置,才能執行後續操做。

添加的操做就很少說了,邏輯上和普通鏈表差很少,無非是普通鏈表只是插入一層,而跳躍鏈表插入首先肯定新插入節點的層級m(可使用隨機數肯定,保持層級節點比例便可),而後將節點插入到每一層鏈表便可。

總結:

與更高級的數據結構相比,例如自適應樹或者AVL樹,跳躍鏈表的效率至關不錯,所以,用跳躍鏈表來代替這些數據結構是可行的的。

  • 自組織鏈表

鏈表的數據結構探討已經差很少了,事實上,咱們上述探討的是鏈表的靜態結構,研究的是基於已經組織好的鏈表的算法。咱們還可使用某種方法動態地組織鏈表,從而提升查找效率。經過動態方式組織的鏈表,能夠稱之爲自組織鏈表。

這裏給出4種動態組織鏈表的方式:

  1. 前移法。在找到須要的元素以後,把它放到鏈表的開頭。
  2. 換位法。在找到須要的元素以後,只要它不在鏈表的開頭,就與其前驅交換位置。
  3. 計數法。根據元素被訪問的次數,對鏈表進行排序。
  4. 排序法。根據節點元素的屬性,對鏈表進行排序。

咱們經過一組表格來講明這4中方法:

假設如今有一個空鏈表以及一段數據流-ACBCDADACACCEE,咱們經過上面4中方式,將數據流動態的組織到鏈表中。

爲了測試這些方法的效率,能夠將實際比較次數與可能的最大比較次數相對比。可能的最大比較次數是將鏈表在處理每一個元素時的長度相加獲得的。好比在上面表格中,數據體包含14個字母,在處理每一個字母前,將鏈表的長度記錄下來,其結果是0+1+2+3+3+4+4+4+4+4+4+4+4+5=46,這個總長度用來與所作的比較次數相對比。對於全部的方法來講,這個總長度是相同的,並且是最大的,那麼就能夠用來作對比了。

咱們經過將不一樣文本中的單詞組織成鏈表來測試上述方法的查找效率:

咱們將普通法、前移法、換位法經過兩種不一樣的插入方式進行比較。經過以上數據總結出如下特徵:

  • 隨着數據量的提高,全部方法的效率都有所提升
  • 在尾部插入數據老是比在前端插入數據高效
  • 跳躍鏈表>計數法>前移法>換位法

能夠看到跳躍鏈表的查詢效率相比於其餘方法有着巨大的優點,緊隨其後的是計數法,而後是前移法以及換位法都比字母順序以及普通法表現優異。

並非說查詢效率越高的就越好,跳躍鏈表的組織方式相比於其餘方法要複雜不少。事實上,計數法在軟件開發中使用的不少,咱們日常見到的‘熱數據’本質上屬於計數法。這些方法組織鏈表的方式簡單、高效,每每會獲得大量應用。

這裏有一點須要注意,在上述表格中只包含了數據的比較,沒有包含執行這些方法所須要的其餘操做,若是包含這些信息,這些方法之間的差別可能就不會那麼顯著了。

這裏簡單總結一下,對於中等數量的數據,使用鏈表就足夠了。隨着數據量的增加和訪問頻率的提升須要使用更復雜的方法和數據結構。

到此爲止,鏈表的探討就結束了,更多的收穫就須要在實踐中摸索了。

數據結構與算法-棧與隊列

相關文章
相關標籤/搜索