Redis跳跃表的实现
在看redis代码的时候,看到了redis实现了一个跳跃表,看了一下结构,写了一个小demo,记录一下,代码链接;
跳跃表结构
跳跃表以有序的方式在层次化的链表中保存元素, 效率和平衡树媲美 —— 查找、删除、添加等操作都可以在对数期望时间下完成, 并且比起平衡树来说, 跳跃表的实现要简单直观得多。

从图中可以看到, 跳跃表主要由以下部分构成:
- 表头:负责维护跳跃表的节点指针;
- 跳跃表节点:保存着元素值,以及多个层;
- 层:保存着指向其他元素的指针。高层的指针越过的元素数量大于等于低层的指针,为了提高查找的效率,程序总是从高层先开始访问,然后随着元素值范围的缩小,慢慢降低层次。
- 表尾:全部由 NULL 组成,表示跳跃表的末尾。
跳跃表实现
跳跃表结构体
跳跃表节点定义
//
struct SkipNode {
int _key;
// 定义一个指针数组
SkipNode **_forward;
// 指针数组的初始化全部赋0,即nullptr
SkipNode(int key, int level) : _key(key) {
_forward = new SkipNode *[level + 1];
memset(_forward, 0, sizeof(SkipNode *) * (level + 1));
}
~SkipNode() {
delete[] _forward;
}
};
跳跃表链表定义
class SkipList {
public:
explicit SkipList(int maxLevel, float p) : _maxLevel(maxLevel), _level(0), _p(p), _size(0) {
// 初始化创建header的level为最大level,并将值赋为INT_MIN
header = new SkipNode(INT_MIN, maxLevel);
}
~SkipList() {
delete header;
}
void addNode(int key);
void delNode(int key);
void searchNode(int key);
void display();
size_t getSize() const { return _size; }
private:
int randomLevel();
private:
int _maxLevel;
SkipNode *header;
float _p;
int _level;
size_t _size;
};
跳跃表实现
#include "SkipList.h"
#include <iostream>
void SkipList::addNode(int key) {
// 用一个具体的update存储在每一层中,新node要存储的位置;
SkipNode *update[_maxLevel + 1];
SkipNode *current = header;
memset(update, 0, sizeof(SkipList *) * (_maxLevel + 1));
// 将现有的层中,需要插入新node的位置记录在update中;
for (int i = _level; i >= 0; i--) {
while (current->_forward[i] != nullptr && current->_forward[i]->_key < key)
current = current->_forward[i];
update[i] = current;
}
// 获取当前节点的后一个节点,即寻找的那个元素
current = current->_forward[0];
// 如果后一个为空或者后一个值不等于key,说明key还不再当前的跳表中,执行添加操作
if (current == nullptr || current->_key != key) {
// 获取一个随机的level
int level = randomLevel();
// 如果获取的随机level比当前的level大的话,将update[i]在[_level+1,level]之间赋值为header
if (level > _level) {
for (int i = _level + 1; i < level + 1; ++i)
update[i] = header;
_level = level;
}
std::cout << "level:" << level << std::endl;
auto node = new SkipNode(key, level);
// 新建值为key的node,在获取的随机level层链表都插入
for (int i = level; i >= 0; i--) {
node->_forward[i] = update[i]->_forward[i];
update[i]->_forward[i] = node;
}
_size++;
std::cout << "add succeed, key: " << key << std::endl;
}
}
// 删除节点的操作大部分类似与添加节点
void SkipList::delNode(int key) {
SkipNode *update[_maxLevel + 1];
SkipNode *current = header;
memset(update, 0, sizeof(SkipList *) * (_maxLevel + 1));
for (int i = _level; i >= 0; --i) {
while (current->_forward[i] != nullptr && current->_forward[i]->_key < key) {
current = current->_forward[i];
}
update[i] = current;
}
current = current->_forward[0];
// 判断下一个不为空,且下一个的值等于key
if (current != nullptr && current->_key == key) {
for (int i = 0; i <= _level; i++) {
// 判断寻找的update[i]后一个是否为current,不是的话,就可以跳出循环了,因为是从0层开始的
if (update[i]->_forward[i] != current)
break;
update[i]->_forward[i] = current->_forward[i];
}
delete current;
while (_level > 0 && header->_forward[_level] == nullptr)
_level--;
--_size;
std::cout << "del succeed, key: " << key << std::endl;
}
}
// 遍历节点,并打印
void SkipList::display() {
for (int i = _level; i >= 0; --i) {
//std::cout << "level:" << i << std::endl;
SkipNode *node = header->_forward[i];
while (node) {
std::cout << node->_key << " ";
node = node->_forward[i];
}
std::cout << std::endl;
}
}
// 获取一个随机的level,可以根据传入的p值,层数越高,概率越小,概率为p^n
int SkipList::randomLevel() {
float p = (float) random() / RAND_MAX;
int level = 0;
while (p < _p && level < _maxLevel) {
++level;
p = (float) random() / RAND_MAX;
}
return level;
}
// 寻找跳表中是否有该节点,在添加和删除节点中,涉及到该步骤
void SkipList::searchNode(int key) {
SkipNode *current = header;
for (int i = _level; i >= 0; --i) {
while (current->_forward[i] != nullptr && current->_forward[i]->_key < key)
current = current->_forward[i];
}
current = current->_forward[0];
if (current && current->_key == key) {
std::cout << "Found key: " << key << std::endl;
} else {
std::cout << "Not Found key " << key << " in list" << std::endl;
}
}
int main() {
SkipList lst(3, 0.5);
for (int i = 0; i < 50; ++i)
lst.addNode(i);
lst.display();
std::cout << "SkipList size:" <<lst.getSize() << std::endl;
lst.delNode(19);
lst.display();
lst.searchNode(20);
lst.searchNode(300);
std::cout << "SkipList size:" << lst.getSize() << std::endl;
}
跳跃表功能验证
运行上述程序的main函数,打印如下内容:
......
level:3
13 14 32
level:2
4 13 14 26 30 32 33 41 47 49
level:1
1 4 5 6 7 12 13 14 16 19 21 22 25 26 28 30 32 33 36 37 41 44 46 47 49
level:0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
del succeed, key: 19
level:3
13 14 32
level:2
4 13 14 26 30 32 33 41 47 49
level:1
1 4 5 6 7 12 13 14 16 21 22 25 26 28 30 32 33 36 37 41 44 46 47 49
level:0
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
Found key: 20
Not Found key 300 in list

821

被折叠的 条评论
为什么被折叠?



