跳躍表(Skip Lists)是一種有序的數據結構,它通過在每個節點中維持多個指向其他節點的指針,從而達到快速訪問節點的目的。在大部分情況下,跳躍表的效率可以和平衡樹相媲美,並且在實現上比平衡樹要更為簡單,因而得到了廣泛的應用。
如上圖所示,是一個跳躍表的示例。由此可以看出跳躍表的幾個特點:
-
有序性,如上圖中各節點呈遞增趨勢;
-
跳躍表由多個層組成;
-
跳躍表的第一層始終包含所有元素;
-
如果某個元素位於第 i 層,那該層一下的所有層也會包含此元素。
既然是鏈表的一種,那在實現時,固然要考慮插入、刪除、查找等幾個主要方法,下面來一一解析。
查找
要查找一個元素,應從頂層開始,自頂向下查找,又因為其有序性,因此與平衡樹上的查找其實很類似。
以之前的圖為例,要搜索 12,從頂層為入口,步驟大致如下:
-
從 L3 的第一個節點查詢后一個節點 21,發現比 12 大,跳至下一層;
-
從 L2 的第一個節點查詢后一個節點 9,比 12 小,因此繼續向后;
-
繼續查詢 L2 的下一個節點 21,比 12 大,跳至下一層;
-
從 L1 的第一個節點開始查詢,直至 17 時發現比 12 大,再次調至下一層;
-
從 L0 的第一個節點開始查詢,最終發現 12 這個節點,查詢結束。
如果要查詢的節點在 L0 中都不存在,則應返回並告知“該節點不存在”。
插入
要插入一個節點,不難想象首先需要查詢插入的位置,因此首先其實是類似的執行一次查詢。然后視情況定是做替換操作還是新增節點。插入的時候要利用一個隨機算法來獲取該元素要插入的層高,並根據“如果某個元素位於第 i 層,那該層一下的所有層也會包含此元素”這一特性,在要插入層和以下層中都要插入這個新元素。最后還要注意維護層高。以下是插入過程的一個示例:
刪除
刪除的第一步和插入很類似,首先要執行查找過程。如果查找到,則將該元素刪除。這里也必須注意“如果某個元素位於第 i 層,那該層一下的所有層也會包含此元素”這一特性。最后還要注意維護層高。
性能上,跳躍表支持平均 O(log N)、最壞 O(N) 復雜度的節點查找,還可以通過順序性操作來批量處理節點。
代碼實現在網上有很多代碼可以參考,且實現方法和細節也不盡相同,因此不在這里提及,而是更多的關注原理。有時候掌握基礎與原理其實更為重要。
本文的插圖來自於 William Pugh 關於跳躍表的論文《Skip Lists: A Probabilistic Alternative to Balanced Trees》,其中分析了跳躍表的實現及性能,值得研讀。
——————————
推薦閱讀: