leveldb(五):SkipList跳表
来源:互联网 发布:php json格式化输出 编辑:程序博客网 时间:2024/05/22 14:44
跳表原理的简单介绍
跳表是平衡树的一种替代的数据结构,但是和红黑树不相同的是,跳表对于树的平衡的实现是基于一种随机化的算法的,这样也就是说跳表的插入和删除的工作是比较简单的。
下面来研究一下跳表的核心思想:
先从链表开始,如果是一个简单的链表,那么我们知道在链表中查找一个元素I的话,需要将整个链表遍历一次。
如果是说链表是排序的,并且节点中还存储了指向前面第二个节点的指针的话,那么在查找一个节点时,仅仅需要遍历N/2个节点即可。
跳表的核心思想,其实也是一种通过“空间来换取时间”的一个算法,通过在每个节点中增加了向前的指针,从而提升查找的效率。
跳表的数据存储模型
我们定义:
如果一个基点存在k个向前的指针的话,那么陈该节点是k层的节点。
一个跳表的层MaxLevel义为跳表中所有节点中最大的层数。
下面给出一个完整的跳表的图示:
那么我们该如何将该数据结构使用二进制存储呢?通过上面的跳表的很容易设计这样的数据结构:
定义每个节点类型:
// 这里仅仅是一个指针
typedef struct nodeStructure *node;
typedef struct nodeStructure
{
keyType key; // key值
valueType value; // value值
// 向前指针数组,根据该节点层数的
// 不同指向不同大小的数组,指向的是不同层的后面节点
node forward[1];
};
我们在来看看leveldb中跳表节点的定义:
template<typename Key, class Comparator>struct SkipList<Key,Comparator>::Node { explicit Node(const Key& k) : key(k) { } Key const key; Node* Next(int n) { return reinterpret_cast<Node*>(next_[n].Acquire_Load()); } void SetNext(int n, Node* x) { next_[n].Release_Store(x); } Node* NoBarrier_Next(int n) { return reinterpret_cast<Node*>(next_[n].NoBarrier_Load()); } void NoBarrier_SetNext(int n, Node* x) { next_[n].NoBarrier_Store(x); } private: port::AtomicPointer next_[1];}
总体上与我们上面所说的存储模型结构一样,只不过这里的next_用了AtomicPointer ,关于AtomicPointer 请参考前面写的博客,主要起到同步的作用。Next的函数是为了读取第n层的下一个节点,而SetNext则是设置第n层的下一个节点是谁。
再来看下leveldb跳表的定义:
class SkipList { private: struct Node; private: enum { kMaxHeight = 12 };//跳表最高多少层 Comparator const compare_;//比较节点key大小的 Arena* const arena_; //为节点分配空间用的 Node* const head_; //指向表头 // Modified only by Insert(). Read racily by readers, but stale // values are ok. port::AtomicPointer max_height_; //目前跳表最高多少层 Random rnd_; //用来生成每个节点层数的随机数生成器 ...}
跳表的代码实现分析
template<typename Key, class Comparator>SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena) : compare_(cmp), arena_(arena), head_(NewNode(0 , kMaxHeight)),//头部节点初始化为具有最高层数的节点 max_height_(reinterpret_cast<void*>(1)),//但当前跳表高度初始化为1,就是只有最下面一层 rnd_(0xdeadbeef) { for (int i = 0; i < kMaxHeight; i++) { head_->SetNext(i, NULL); }}template<typename Key, class Comparator>typename SkipList<Key,Comparator>::Node*SkipList<Key,Comparator>::NewNode(const Key& key, int height) { char* mem = arena_->AllocateAligned( sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1)); //这种分配方式很有特色 return new (mem) Node(key);}
插入操作
template<typename Key, class Comparator>void SkipList<Key,Comparator>::Insert(const Key& key) { Node* prev[kMaxHeight]; //从最高层往下依次寻找各层大于或等于key的节点,并把各层前一个节点保存在prev中便于插入 Node* x = FindGreaterOrEqual(key, prev); //随机生成几点层数,RandomHeight保证不会超过跳表规定的最高层kMaxHeight int height = RandomHeight(); if (height > GetMaxHeight()) { //把大于原来跳表高度的层的prev节点设为头,最后更新跳表高度 for (int i = GetMaxHeight(); i < height; i++) { prev[i] = head_; } max_height_.NoBarrier_Store(reinterpret_cast<void*>(height)); } 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)); //这里使用了内存屏障,保证了下面这条setnext一定在上面那个setnext后发生, //想想如果后面这个先发生会发生什么,先将prev的下一个节点设为了newNode,但 //newNode此时的next为null,无法继续读后面的节点,所以一定是先修改了newNode //的next后在把newNode插入prev。 prev[i]->SetNext(i, x); } }
因为leveldb的跳表是作为memtable使用的,上层只有插入没有删除操作,因为上层的删除操作其实也就是插入操作而以,所以这里就不分析delete操作,其实原理与insert差不多。
其他操作也比较简单,就不分析了。
跳表的迭代器
class Iterator { public: explicit Iterator(const SkipList* list); bool Valid() const; const Key& key() const; //返回迭代器所处位置节点的值 void Next(); void Prev(); void Seek(const Key& target); void SeekToFirst();//到跳表头 void SeekToLast();//到尾 private: const SkipList* list_;//迭代器所迭代的跳表 Node* node_;//当前迭代器所处位置的节点 }
实现也比较简单,看名字就知道啥意思了。
template<typename Key, class Comparator>inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) { node_ = list_->FindGreaterOrEqual(target, NULL);}
seek就是在跳表中找到第一个key大于或等于target的元素。
- leveldb(五):SkipList跳表
- Leveldb --- SkipList
- LevelDb skiplist
- leveldb 源码分析 —— SkipList跳表
- LevelDB源码分析之五:skiplist(1)
- levelDB源码分析-Skiplist
- leveldb阅读-Skiplist
- leveldb学习:skiplist
- leveldb源码解析7——跳表(SkipList)
- LevelDB源码之SkipList原理LevelDB源码之SkipList原理
- leveldb源代码分析4:SkipList
- LevelDB源码之SkipList原理
- LevelDB源码之SkipList原理
- LevelDB源码之SkipList原理
- LevelDB源码剖析之SkipList
- LevelDB源码剖析之SkipList
- leveldb对skiplist的使用
- levelDB源码笔记(4)- SkipList
- HDU
- android stuido集成jni
- HTTPS原理及交互过程
- 【Nginx】负载均衡
- 关于静态变量编译的问题
- leveldb(五):SkipList跳表
- 剑指Offer读书笔记(3)
- Do you think the advantages of charging people for admission to museums outweigh the disadvantages?
- thinkphp mvc模式快速理解应用
- 安卓开发中小米手机android studio调试问题
- Numpy入门学习之(五)数组、矩阵中级操作
- Openev3 python基础学习4——基础滤波算法
- java基本数据类型
- 聊聊线程池