什麼是skip list?
“Skip lists are a data structure that can be used in place ofbalanced trees. Skip lists use probabilistic balancing rather than strictlyenforced balancing and as a result the algorithms for insertion and deletion inskip lists are much simpler and significantly faster than equivalent algorithmsfor balanced trees.“
如上爲skip list作者William Pugh對skip list的解釋,跳錶是一種可以替代平衡樹的數據結構,跳錶使用的平衡算法並不是嚴格要求的平衡,這使插入和刪除操作是更簡單,並且明顯比平衡樹快速。
跳錶數據結構
如上圖中的a鏈表,是一個有序鏈表,如果從中找一個節點,最多可能需要遍歷每一個節點,那麼時間複雜度就是O(n);n爲鏈表中節點的個數。
如上圖中的b鏈表,每隔一個節點多了一層指針,指向它前面第二2個節點,那麼我們要查找一個節點,最多可能需要遍歷(n/2)+ 1個節點,時間複雜度就是O(n/2);
如上圖中的c鏈表,在b鏈表的基礎上多了一層指針,每第四個節點,指向它前面第四個節點,那麼我們要查找一個節點,最多可能需要遍歷(n/4)+ 2個節點,時間複雜度就是O(n/4);
如上圖中的d鏈表,如果每第2^i個節點有指針指向它前面第2^i個節點,那麼查找某個節點的時間複雜度可以降低到O(logn),這種結構查詢起來很快速,但是插入和刪除確是不切實際的。
一個節點有k個向前的指針,那麼就稱這個節點爲k層節點,每個節點的層數是隨機產生的,產生level 1的概率是50%,level2的概率是25%,level 3的概率是12.5%……,每個節點的層數的產生要保證採用這種相同的機制;每個節點的第i層指針,指向下一個至少有i層的節點。因爲會滑過一些中間節點,所以稱爲跳錶。
跳錶算法
查找(僞碼如下)
Search(list, searchKey)
x :=list→header
--loop invariant: x → key < searchKey
for i := list→level downto 1 do
while x →forward[i]→ key < searchKey do
x := x →forward[i]
-- x→ key < searchKey ≤ x→ forward[1] → key
x:= x →forward[1]
if x →key = searchKey then return x → value
else return failure
從頂層開始找,每層中尋找比目標小的最大的一個節點,然後轉到下一層,然後向前移動一個節點,如果相等就代表找到了,如果不相等,就代表沒有這個節點。
插入(僞碼如下)
Insert(list, searchKey, newValue)
local update[1..MaxLevel]
x :=list→header
for i := list→level downto 1 do
while x →forward[i]→ key < searchKey do
x := x →forward[i]
-- x →key < searchKey ≤ x →forward[i] →key
update[i] := x
x:= x →forward[1]
if x →key = searchKey then x → value := newValue
else
lvl := randomLevel()
if lvl > list → level then
for i := list→level + 1 to lvl do
update[i] := list→header
list→ level := lvl
x := makeNode(lvl, searchKey, value)
for i := 1 to level do
x →forward[i] := update[i] →forward[i]
update[i] →forward[i] := x
從頂層開始找,每層中尋找比目標小的最大的一個節點,並記錄該節點到update數組,如果新節點的層數大於之前的,則更新list→level,並將list→header放到update數組,其實update數組存放的就是i層中指向目標節點(正插入)的前一個節點,所以最後就是循環每一層,將每層中指向目標的前一節點的對應指針指向目標節點,目標節點的下一個節點指向前一個節點的下一個節點。
刪除(僞碼如下)
Delete(list, searchKey)
local update[1..MaxLevel]
x := list→header
for i := list→ level downto 1 do
while x→forward[i]→ key < searchKey do
x := x →forward[i]
update[i]:= x
x := x →forward[1]
if x →key = searchKey then
for i := 1 to list → level do
if update[i] →forward[i] ≠ x then break
update[i]→forward[i] := x→ forward[i]
free(x)
while list→level > 1 and
list→header→ forward[list →level] = NIL do
list→level := list →level – 1
從頂層開始找,每層中尋找比目標小的最大的一個節點,並記錄該節點到update數組,循環每層,將update中指向目標節點(被刪除的節點)的指針指向目標節點的下一個節點;如果被刪節點的層數是最高的,就要更新list→level,減去高出的部分。