Redis跳跃表的实现

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
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值