探索Skip List (跳躍表)
阿新 • • 發佈:2018-01-20
mark 鏈表 基本 來看 http earch void 速度 解釋
,level為1的那層就是單純的有序單鏈表,隨著層次遞增,元素會越來越少。比如level的取值範圍可以是
附William Pugh的論文 《Skip Lists: A Probabilistic Alternative to Balanced Trees》
寫在前面
以下內容針對的是Skip List的插入和刪除,建議你先到其他地方大概了解一下Skip List長什麽樣子的,然後再過來看看這篇,最好還是看一眼論文先,部分挺容易看懂的。Redis中的Sorted Set基本就是使用Skip List,只是稍作修改。
初識 Skip List
Skip List 是一種數據結構,實質上為一個鏈表,專門用於存儲有序元素,提供的查找速度可與平衡二叉樹媲美,優點是實現簡單。
論文中Skip List就是長上面這樣的,每個節點有多個forward指針,指向在其後面的元素。將forward指針分層,稱為level
[1, 32]
。
Skip List 的插入
先快速看一眼下面翻譯過來的偽碼實現。
void Insert(list, searchKey, newValue) { local update[1..MaxLevel]; x = list->header; // 查找searchKey應存放的位置 for(i = list->level to 1) { while(x->forward[i]->key < searchKey) x = x->forward[i]; // 位置關系: x->key < searchKey <= x->forward[i]->key update[i] = x; // 看上行註釋便知update保存的是什麽 } x = x->forward[1]; // 這在最低層 if(x->key == searchKey) { // 已有相同的key,替換即可 x->value = newValue; } else { lv = randomLevel(); // 為新節點隨機取個level if(lv > list->level) // 特殊處理:新節點level比當前最大level高 { for(i = list->level+1 to lv) update[i] = list->header; list->level = lv; } x = createNode(v, searchKey, newValue); for(i = 1 to lv) // 調整相關指針 { x->forward[i] = update[i]->forward[i]; update[i]->forward[i] = x; } } }
實現原理是,用一個update
數組保存"最大且小於searchKey的元素",用它來調整涉及到的指針指向。搜索時從高層往低層搜索,順便記錄update數組,調整指針時從低層往高層調整。可能出現的情況是,新節點的level大於原來list的最大level,此時需要更新一下list的最大level。
randomLevel()
比較容易實現,就是拋硬幣法,返回一個數字n表示拋了n+1次才出現反面,但要求n<=MaxLevel。
Skip List 的刪除
void Delete(list, searchKey) { int update[1..MaxLevel]; x = list->header; // 查找searchKey的存放位置 for(i = list->level to 1) { while(x->forward[i]->key < searchKey) x = x->forward[i]; update[i] = x; } x = x->forward[i]; if(x->key == searchKey) // 若命中,則刪 { // 調整指向x的指針 for(i = 1 to list->level) { if(update[i]->forward[i] != x) break; update[i]->forward[i] = x->forward[i] } free(x); // 可能需要更新list的max level while(list->level > 1 && !list->header->forward[list->level]) list->level = list->level - 1; } }
看過Insert之後,這個不用解釋也能看懂了。
探索Skip List (跳躍表)