跳躍列表是一種數據結構。他允許快速查詢一個有序連續元素的數據鏈表。平均查找和插入的事件複雜度爲O(log(n)),優於普通鏈表O(n)。快速查詢是通過維護一個多層次的鏈表,並且每一層鏈表中的元素是前一層鏈表元素的子集。
具體描述:
在levelDb中跳躍列表每個節點的數據結構如下所示:
template <typename Key, class Comparator>
struct SkipList<Key, Comparator>::Node {
explicit Node(const Key& k) : key(k) {}
Key const key;
// Accessors/mutators for links. Wrapped in methods so we can
// add the appropriate barriers as necessary.
Node* Next(int n) {
assert(n >= 0);
// Use an 'acquire load' so that we observe a fully initialized
// version of the returned Node.
return next_[n].load(std::memory_order_acquire);
}
void SetNext(int n, Node* x) {
assert(n >= 0);
// Use a 'release store' so that anybody who reads through this
// pointer observes a fully initialized version of the inserted node.
next_[n].store(x, std::memory_order_release);
}
// No-barrier variants that can be safely used in a few locations.
Node* NoBarrier_Next(int n) {
assert(n >= 0);
return next_[n].load(std::memory_order_relaxed);
}
void NoBarrier_SetNext(int n, Node* x) {
assert(n >= 0);
next_[n].store(x, std::memory_order_relaxed);
}
private:
// Array of length equal to the node height. next_[0] is lowest level link.
std::atomic<Node*> next_[1];
};
可以看出 std::atomic<Node*> next_[1]成員就可以構建跳錶的層數。
其中層數是一個隨機數。
跳躍列表不像平衡樹等數據結構那樣提供對最壞情況的性能保證:由於用來建造跳躍列表採用隨機選取元素進入更高層
的方法,在小概率情況下會生成一個不平衡的跳躍列表(最壞情況例如最底層僅有一個元素進入了更高層,此時跳躍列
表的查找與普通列表一致)。但是在實際中它通常工作良好,隨機化平衡方案也比平衡二叉查找樹等數據結構中使用的
確定性平衡方案容易實現。跳躍列表在並行計算中也很有用:插入可以在跳躍列表不同的部分並行地進行,而不用對數
據結構進行全局的重新平衡。
實現細節:
因爲跳躍列表中的元素可以在多個列表中,所以每個元素可以有多於一個指針。跳躍列表的插入和刪除的實現與普通的鏈表操作類似,但高層元素必須在進行多個鏈表中進行插入或刪除。
跳躍列表的最壞時間性能具有一定隨機性,但是可以通過時間複雜度爲 O(n)的遍歷操作(例如在打印列表全部內容時)以無隨機的算法重整列表的結構,從而使跳躍列表的實際查找時間複雜度儘量符合理論平均值 O(log(n))。
插入操作:
先定義 記錄已經遍歷的節點的指針數組,開始找好合適的位置進行插入操作。即調用FindGreaterOrEqual。
template <typename Key, class Comparator>
void SkipList<Key, Comparator>::Insert(const Key& key) {
// TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()
// here since Insert() is externally synchronized.
//記錄插入節點插入位置的前一個節點
Node* prev[kMaxHeight];
//找合適的插入位置
Node* x = FindGreaterOrEqual(key, prev);
// Our data structure does not allow duplicate insertion
assert(x == nullptr || !Equal(key, x->key));
//跟要插入節點確定一個層高
int height = RandomHeight();
//比當前最高層要高
if (height > GetMaxHeight()) {
//將最高層記錄指向head_
for (int i = GetMaxHeight(); i < height; i++) {
prev[i] = head_;
}
// It is ok to mutate max_height_ without any synchronization
// with concurrent readers. A concurrent reader that observes
// the new value of max_height_ will see either the old value of
// new level pointers from head_ (nullptr), or a new value set in
// the loop below. In the former case the reader will
// immediately drop to the next level since nullptr sorts after all
// keys. In the latter case the reader will use the new node.
max_height_.store(height, std::memory_order_relaxed);
}
//創建新的節點,這裏使用內存池根據傳入的層高給新節點分配空間
//並將相應的鍵值設置好
x = NewNode(key, height);
//相鏈表插入新的節點一樣插入,這裏從底層開始,指向下一個節點的對應層次上
for (int i = 0; i < height; i++) {
// NoBarrier_SetNext() suffices since we will add a barrier when
// we publish a pointer to "x" in prev[i].
x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));
prev[i]->SetNext(i, x);
}
}
//在鏈表中找合適的插入點
template <typename Key, class Comparator>
typename SkipList<Key, Comparator>::Node*
SkipList<Key, Comparator>::FindGreaterOrEqual(const Key& key,
Node** prev) const {
//先記錄跳躍表的頭指針
Node* x = head_;
//獲取跳躍表的層數
int level = GetMaxHeight() - 1;
//從較高節點處開始,遍歷跳躍表
while (true) {
Node* next = x->Next(level);
//將當前節點鍵值和插入的鍵值進行比較
if (KeyIsAfterNode(key, next)) {
// Keep searching in this list
//不符合條件繼續找
x = next;
}
//找到了
else {
//找到了,通過prev指針數組將節點記錄下來
if (prev != nullptr) prev[level] = x;
//將當前節點leve減爲0的時候將next返回
if (level == 0) {
return next;
} else {
// Switch to next list
level--;
}
}
}
}
template <typename Key, class Comparator>
bool SkipList<Key, Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {
// null n is considered infinite
//節點不爲空,將當前鍵值和插入鍵值進行比較
return (n != nullptr) && (compare_(n->key, key) < 0);
}
自己實現的跳錶,使用智能指針,無需考慮內存釋放問題:
skipList.h
#pragma once
#include <vector>
#include <iostream>
#include <cassert>
#include <stdint.h>
#include <memory>
#define TYPE template <typename keyType, typename valType>
#define CLASS Node<keyType, valType>
#define SKIPCLASS skipList<keyType, valType>
using namespace std ;
//使用leveldb中的隨機數生成器
class Random {
private:
uint32_t seed_;
public:
explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) {
// Avoid bad seeds.
if (seed_ == 0 || seed_ == 2147483647L) {
seed_ = 1;
}
}
uint32_t Next() {
static const uint32_t M = 2147483647L; // 2^31-1
static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0
uint64_t product = seed_ * A;
seed_ = static_cast<uint32_t>((product >> 31) + (product & M));
if (seed_ > M) {
seed_ -= M;
}
return seed_;
}
uint32_t Uniform(int n) { return (Next() % n); }
bool OneIn(int n) { return (Next() % n) == 0; }
uint32_t Skewed(int max_log) {
return Uniform(1 << Uniform(max_log + 1));
}
};
TYPE
class Node {
public :
Node() {}
~Node() {}
public :
keyType key ;
valType value ;
//後繼指針數組
vector<shared_ptr<CLASS>>next ;
};
TYPE
class skipList {
public:
skipList():level(1), head(nullptr), rnd(0xdeadbeef) {}
~skipList() {}
shared_ptr<SKIPCLASS> createSkipList() ;
shared_ptr<CLASS> createNode(int level, keyType key, valType val) ;
bool insert(shared_ptr<SKIPCLASS>sl, keyType key, valType val) ;
valType* search(shared_ptr<SKIPCLASS>sl, keyType key) ;
bool erase(shared_ptr<SKIPCLASS>sl, keyType key) ;
int getLevel() ;
public :
int level ; //層數
shared_ptr<CLASS> head ;
//最大層數
Random rnd ;
};
//隨機產生當前跳錶的層數
TYPE
int SKIPCLASS :: getLevel() {
static const unsigned int kBranching = 4;
int height = 1;
while (height < level && ((rnd.Next() % kBranching) == 0)) {
height++;
}
assert(height > 0);
assert(height <= level);
return height;
}
//創建一個新的跳錶節點
TYPE
shared_ptr<CLASS> SKIPCLASS :: createNode(int level, keyType key, valType val) {
//創建節點
shared_ptr<CLASS> p = make_shared<CLASS>() ;
//跳錶的層空間
p->next.reserve(level) ;
//申請層數
//設置鍵值
p->key = key ;
p->value = val ;
return p ;
}
//創建跳錶
TYPE
shared_ptr<SKIPCLASS> SKIPCLASS :: createSkipList() {
//創建新的跳錶
shared_ptr<SKIPCLASS> sl = make_shared<SKIPCLASS>() ;
//預設跳錶層數爲0
//創建跳錶節點
int level = getLevel() ;
//設置高度
sl->level = level ;
shared_ptr<CLASS> h = createNode(level, 0, 0) ;
sl -> head = h ;
//將head的next數組清空
for(int i=0; i<level; i++) {
h->next[i] = nullptr ;
}
srand(time(0)) ;
return sl ;
}
//跳錶的插入操作
TYPE
bool SKIPCLASS :: insert(shared_ptr<SKIPCLASS>sl, keyType key, valType val) {
cout << "插入元素:" << endl ;
vector<shared_ptr<CLASS>>update ;
update.reserve(level) ;
shared_ptr<CLASS> q, p = sl->head ;
//從最高層開始,進行搜索
int i=sl->level-1;
for(;i>=0;i--) {
//q->next[i]不爲空並且p->next[i]中的key小於插入的key
while((q=p->next[i])&&q->key<key) {
p=q ;
}
//找打了插入點的前一個節點保存
update[i] = p ;
}
//key等於插入的key,只修改相應的值
if(q && q->key == key) {
q->value = val ;
return true ;
}
int level = getLevel() ;
//產生的隨機數比跳錶的層數大,則在update中將新添加的層指向header
if(level > sl->level) {
//擴增update
update.reserve(level) ;
//使得插入點的前一個節點保存頭結點,頭結點在跳躍表中的level應該是最高的
for(int i=sl->level; i<level; i++) {
update[i] = sl->head ;
}
//設置跳錶的層數爲level
sl->level = level ;
}
//創建一個節點
q = createNode(level, key, val) ;
//新建一個待插入節點,前一個節點一層層插入
for(int i=level-1; i>=0; --i) {
q->next[i] = update[i]->next[i] ;
update[i]->next[i] = q ;
}
cout << "插入元素成功~!" << endl ;
return true ;
}
//調錶刪除節點操作
TYPE
bool SKIPCLASS :: erase(shared_ptr<SKIPCLASS> sl, keyType key) {
vector<shared_ptr<CLASS>> update ;
shared_ptr<CLASS>q = nullptr, p = sl->head ;
update.reserve(level) ;
int i = sl->level -1 ;
for(; i>=0; --i) {
while((q = p->next[i]) && q->key < key) {
p = q ;
}
update[i] = p ;
}
//判斷是否爲待刪除的鍵
if(!q || (q&&q->key != key)) {
return false ;
}
//逐層刪除
for(i=sl->level-1; i>=0; --i) {
if(update[i]->next[i] == q) {
update[i]->next[i] = q->next[i] ;
}
if(sl->head->next[i] == nullptr) {
sl->level -- ;
}
}
q = nullptr ;
return true ;
}
//跳錶的查找
TYPE
valType* SKIPCLASS :: search(shared_ptr<SKIPCLASS>sl, keyType key) {
shared_ptr<CLASS> q, p = sl->head ;
q = nullptr ;
int i = sl->level-1 ;
for(; i>=0; --i) {
while((q = p->next[i]) && q->key < key) {
p = q ;
}
if(q && (key == q->key)) {
return &q->value ;
}
}
return nullptr ;
}
test.cpp
#include "skipList.h"
int main() {
shared_ptr<skipList<int, int>> sl = make_shared<skipList<int, int>>() ;
//創建skiplist
sl = sl->createSkipList() ;
sl->insert(sl, 1, 1) ;
sl->insert(sl, 2, 3) ;
sl->insert(sl, 5, 3) ;
sl->insert(sl, 7, 3) ;
sl->insert(sl, 9, 3) ;
sl->insert(sl, 10, 3) ;
cout << "獲取鍵爲1的值:"<< *sl->search(sl, 1) << endl ;
cout << "獲取鍵爲2的值:" << *sl->search(sl, 2) << endl ;
cout << "刪除鍵爲:1" << " 刪除結果"<< sl->erase(sl, 1) << endl ;
cout << "找鍵爲1的值-------->" << endl ;
if(sl->search(sl, 1) == nullptr) {
cout << "沒找到節點" << endl ;
return 1 ;
} else
cout << "找到了節點!" << endl ;
}
運行結果: