首页 > 其他分享 >跳表原理设计与实现

跳表原理设计与实现

时间:2022-09-06 13:00:51浏览次数:88  
标签:level update current 跳表 key 设计 forward 原理 节点

   

学习方法:类比单链表

和单链表的查找、插入做类似比较

   

核心思路:空间换时间

跳表的核心原理就是 用空间换时间,使得可以以二分的方式来进行节点的搜索

   

我的github: https://github.com/atomxing/skiplist

   

   

   

   

单链表查找很慢 必须遍历所有节点

   

   

   

   

添加部分索引加速查找

template<typename K, typename V>

class Node {

   

public:

Node() {}

Node(K k, V v, int);

~Node();

// 获取key 关键词

K get_key() const;

// 获取value 值

V get_value() const;

void set_value(V);

   

// 线性数组,用于保存指向不同级别的下一个节点的指针

// 意思就是对于一个节点来说,可能会在不同层次然后不同层的下一个节点是什么就存在这个二维数组里面(可以对照OneNote笔记的图片看)

Node<K, V> **forward;

// 代表节点所在层次

int node_level;

private:

K key;

V value;

};

   

   

跳表-使用二分查找的思路

   

   

// 从最高层开始找

for (int i = _skip_list_level; i >= 0; i--) {

while (current->forward[i] && current->forward[i]->get_key() < key) {

current = current->forward[i];

}

}

   

// 到达第0层,并将指针推进到右边的节点,我们搜索这个节点

current = current->forward[0];

   

// 相等,找到了这个节点

if (current and current->get_key() == key) {

std::cout << "Found key: " << key << ", value: " << current->get_value() << std::endl;

return true;

}

   

std::cout << "Not Found Key:" << key << std::endl;

return false;

   

   

   

重建索引开销太大

使用动态更新的方法

template<typename K, typename V>

int SkipList<K, V>::insert_element(const K key, const V value) {

// 为了满足多线程,要加锁

mtx.lock();

// _header是指向头节点的指针

Node<K, V> *current = this->_header;

   

// 创建update数组,并且初始化

// update是一个数组,用于放置node->forward[i](node不同级别的下一个节点),即以后应该被操作的节点。

// 这里是什么原理呢:和单链表做类比,就是待插入位置的前一个节点prev,具体可以看OneNote笔记

Node<K, V> *update[_max_level+1];

memset(update, 0, sizeof(Node<K, V>*)*(_max_level+1));

   

// 从跳表的最高层开始 不断找待插入的位置

for(int i = _skip_list_level; i >= 0; i--) {

// 当前节点(初始为头节点)的下一个节点 不为空

// 并且 当前节点的下一个节点的值 小于要插入的节点指

while(current->forward[i] != NULL && current->forward[i]->get_key() < key) {

current = current->forward[i];

}

// current节点后面就是待插入节点的位置,放到update数组中记录下来

update[i] = current;

}

   

// 达到0级,即最底层,并将指针指向右边的节点,这是想要插入的键。

current = current->forward[0];

   

// 如果当前节点的键值等于搜索到的键值,我们就得到它

if (current != NULL && current->get_key() == key) {

std::cout << "key: " << key << ", exists" << std::endl;

mtx.unlock();

return 1;

}

   

// 如果current是NULL,意味着我们已经到达了本级的终点。

// 如果当前的键不等于键,意味着我们必须在update[0]和当前节点之间插入节点。

// fix 20220831 ylx

else if (current == NULL || current->get_key() != key ) {

   

// 给节点生成一个随机等级

int random_level = get_random_level();

   

// 如果随机等级大于跳表的当前等级,则用头的指针初始化更新值。

// 就是从之前的最高层 到 随机等级之间,这些层次都是空的,将头节点(最底层)作为待插入节点的prev节点

if (random_level > _skip_list_level) {

for (int i = _skip_list_level + 1; i < random_level + 1; i++) {

update[i] = _header;

}

_skip_list_level = random_level;

}

   

// 创建新的节点

Node<K, V>* inserted_node = create_node(key, value, random_level);

   

// 插入节点

// 类比单链表,上面update数组,获取到了待插入位置的前一个节点prev

// inserted_node->next = prev->next (next即forward) (prev即update)

// prev->next = inserted_node

for (int i = 0; i <= random_level; i++) {

inserted_node->forward[i] = update[i]->forward[i];

update[i]->forward[i] = inserted_node;

}

std::cout << "Successfully inserted key:" << key << ", value:" << value << std::endl;

_element_count ++;

}

// 操作完了后解锁

mtx.unlock();

return 0;

}

 

标签:level,update,current,跳表,key,设计,forward,原理,节点
From: https://www.cnblogs.com/libxing/p/16661389.html

相关文章

  • 如何将 Figma 设计转换为 React 代码:分步指南⚛️
    如何将Figma设计转换为React代码:分步指南⚛️你想加速你的React.js应用程序开发吗?正是通过自动将您的设计转换为React组件!如果是,DhiWiseWeb应用程序构建器可以......
  • 导航页面设计/课程
    导航页面设计/课程导航页面设计/课程免费下载在HTML、CSS和JavaScript中HTML:部分导航h1前沿趋势h3.span.loader跨度.mB跨度.mE跨度.mN......
  • gk的树(贪心 dfs) 哈理工程序设计竞赛
    题目:​给你一棵树,每次操作你可以删去一条边,最少需要多少次操作使每个节点的度数都\(<=k\)分析:​我们可以想一想如何贪心,对于本题,最优的结果是让任意一个点连的边最多......
  • C++数据结构课程设计
    C++数据结构课程设计《数据结构》课程设计指导书一、课程设计的目的课程设计为学生提供了一个独立实践的机会,将课本上的理论知识和实际问题结合起来,锻炼学生分析、解决......
  • 设计模式--生成器模式
    简介生成器模式的核心是当构建一个对象的时候,需要包含多个步骤,虽然每个步骤具体的实现不同,但是都遵循一定的流程和规则。比如组装一辆汽车,需要引擎、座位、变速箱、定位器......
  • 设计模式
    1.单例模式:只能创建一个实例的对象。2.单例模式分类两种:饿汉式:类加载就会导致该对象被创建。静态变量的方式:静态代码块方式懒汉式:类加载不会导致该单例对象被创建,而......
  • 字典表设计
    一、业务场景Web项目开发中,字典表的一般都会存在,主要用来给整个系统提供基础服务。比如男女性别的类型可以使用0和1来进行表示,在存储数据和查询数据的时候,就可以使用......
  • 系统设计目标(一):如何提升系统性能?
    提到互联网系统设计,你可能听到最多的词儿就是“三高”,也就是“高并发”“高性能”“高可用”,它们是互联网系统架构设计永恒的主题。在前两节课中,我带你了解了高并发系统设......
  • JVM:第二章:设计一个刚好在一秒堆溢出的程序
    创建了一个JVMDemo类:packagecommon;importjava.lang.management.ManagementFactory;importjava.lang.management.MemoryMXBean;importjava.util.ArrayList;impo......
  • 面向对象的设计原则
    开闭原则(TheOpen-ClosedPrinciple,OCP)软件实体(模块,类,方法等)应该对扩展开放,对修改关闭。系统设计需要遵循开闭原则的原因稳定性。开闭原则要求扩展功能不修改原来的......