跳表的原理及其實現

2021-08-08 19:00:59 字數 3493 閱讀 9281

作用/目的

跳表作為一種資料結構通常用於取代平衡樹。

起因平衡樹可以用於表示抽象的資料型別如字典和有序鍊錶,它通過樹旋轉(tree rotation)操作強制使樹結構保持平衡來保證節點搜尋的效率。在資料為隨機插入的情況下,平衡樹效能表現良好;但資料為順序插入或者需要刪除節點的情況下,平衡樹的效能就會有些糟糕。

跳表可以作為平衡樹的一種替代選擇。它使用隨機的平衡策略取代平衡樹嚴格的強制的樹平衡策略。因此它具有更簡單有效的插入/刪除方法以及更快的搜尋速度。

原理假設有乙個鍊錶,我們要查詢某個節點,則我們需要逐個的查詢鍊錶的每個節點

如果鍊錶是有序的,並且每隔乙個節點都有乙個指向其前面2個位置節點的指標,那我們只需要最多查詢⌈n/2⌉個節點(n為鍊錶長度)

如果再每隔3個節點就有指向其前面4個位置節點的指標,那麼我們就只需要查詢不超過⌈n/4⌉+2個節點

也即如果每個(2^i)位置的節點都有指向其前面2^i個位置節點的指標,則查詢某個節點的次數可以下降到⌈log2^n⌉次(只是指標數會變為之前的雙倍)

這種資料結構可以用於快速的查詢,只是插入和刪除不太容易實現。

如果不再依照節點的位置,而是採取一種隨機的策略來決定節點是否具有額外的指向前面節點的指標呢?

假設擁有k個前向指標的節點我們稱之為k等級節點,在節點被分配出來的時候,我們通過隨機策略(按照一定的概率)來決定節點的等級(也即有幾個前向指標),節點的第i個指標也不再指向其前面2^i個位置的節點,而是指向等級i的下個節點。這樣,插入和刪除節點都只需要做很少的改動,其整體的效果卻和上面所描述的類似。

由於這種資料結構是乙個鍊錶帶有額外的指標,在鍊錶的節點間跳躍,因此,原作者稱其為跳躍鍊錶(skip lists)。

實現/演算法

節點等級

隨機生成節點等級的演算法有很多種,這裡介紹原作者採用的演算法:

首先確定乙個概率p(1/2、1/4等),用於確定節點是否需要有下乙個等級。

就跟投骰子一樣,節點有1/2或1/4的概率獲得下乙個等級,如果是,則節點的等級k=k+1,如果不是,則節點的等級為k,至此結束。

如此重複迴圈。

但這裡會有乙個問題,某些節點的等級k可能會很大(一直獲得下乙個等級,雖然概率極低),這在演算法的原理上沒有問題(除了有極少的效能損耗),但在工程的實現上會相當麻煩,因此,在實際的實現當中,通常會設定乙個最高等級(max_level),並且還會有乙個當前鍊錶最大等級,搜尋的時候從當前最大等級開始。

關於p和max_level取值,原作者推薦的p值是1/4或1/2,max_level可根據所選的p及鍊錶所含的最多元素個數n通過公式logp^n所得。

初始化初始化的時候,我們會分配乙個nil節點(最終節點)並將其key值設為最大int值,還會分配乙個鍊錶初始節點,其header擁有max_level個前向指標,所有的前向指標都初始化成指向nil節點(表明鍊錶中暫無節點)。

搜尋演算法

插入和刪除節點只需要在搜尋的基礎上再進行簡單的插入和刪除操作,只是需要注意兩個操作當中前向指標關係的處理,以及增加和減少鍊錶等級後及時更新當前最大等級的值。插入的過程可見如下示意圖:

c實現**:

[cpp]view plain

copy

#include 

#include 

#include 

#define max_num_of_level 16

#define max_level (max_num_of_level-1)

#define max_int 0x7fffffff /* max integer */

#define max_bits 32 /* 32-bit integers */

typedef

struct _node  node;  

typedef

struct _skiplist  skiplist;  

node *nil;  

int random_bits;  

int bits_left;  

int random()  

node *alloc_node_of_level(int level)  

init()  

int random_level()  

} while (!b);  

return (level > max_level ? max_level : level);  

}  skiplist *new_list()  

void free_list(skiplist *l)  

free(l);  

}  int insert(skiplist *l, int key, int value)  

if (q->key == key)   

/* insert new node */

k = random_level();  

if (k > l->level)   

q = alloc_node_of_level(k);  

q->key = key;  

q->value = value;  

while (k >= 0)   

return 0;  

}  int

delete(skiplist *l, int key)  

if (q->key != key)   

k = 0;  

while (k <= m && (p = update[k])->forward[k] == q)   

free(q);  

while (l->header->forward[m] == nil && m > 0)  

m--;  

l->level = m;  

return 0;  

}  int search(skiplist *l, int key, int &value)  

if (q->key != key)   

*value = value;  

return 0;  

}  /* tests */

int main(int argc, char *argv)  

for (i = 0; i 

for(k = 0; k 

}  free_list(l);  

return 0;  

ARP原理及其實現方法

arp原理及其實現方法 在區域網中,是通過arp協議來完成ip位址轉換為第二層實體地址 即mac位址 的。arp協議對網路安全具有極其重要的意義。通過偽造ip位址和mac位址實現arp欺騙,能夠在網路中產生大量的arp通訊量使網路阻塞。arp協議是 address resolution protoc...

短址原理及其實現

url 演算法google 資料庫瀏覽器 伺服器 前言 最近看了一些關於短址 short url 方面的一些部落格,有些部落格說到一些好的東西,但是,也不是很全,所以,這篇部落格算是對其它部落格的乙個總結吧。介紹 短址,顧名思義,就是把長的 url 轉成短的 url,現在提供這種服務的有很多公司,我...

C C 多型及其實現原理

c c 多型及其實現原理 多型的介紹 多型含義為乙個事物有多種形態。在c 程式設計中,多型性是指具有不同功能的函式可以用同乙個函式名,這樣就可以用乙個函式名呼叫不同內容的函式。一般來說多型分為兩種 靜態多型 也稱為編譯時多型,主要包括引數多型,過載多型和強制多型。引數多型 採用引數化模板,通過給出不...