跳表是平衡树的一种替代的数据结构,和红黑树不同,跳表对树的平衡的实现是基于一种随机化的算法,这样就使得跳表的插入和删除的工作比较简单。 
    跳表是一种复杂的链表,在简单链表的节点信息之上又增加了额外的后继节点指针。这样,单链表每次只能向后移动一个节点,而跳表每个节点有多个后继节点,就可以移动多个不同的距离,加快了查找的速度。

跳表的数据存储结构

定义:如果一个节点存在k个向前的指针的话,则该节点为k层的节点。一个跳表的层MaxLevel为跳表中所有节点层数的最大值。 
下图为一个完整的跳表: 
 
可以设计跳表节点的数据结构:

struct SkipNode{
int key; //跳表节点的key
int value; //跳表节点的val,可以是其他类型
int level; //跳表节点的level,实际的节点level从0 开始,计到level
SkipNode** forward; //跳表节点的后继 SkipNode(int lvl){ //跳表节点的level,则它的level计数从0计到level
level = lvl;
forward = new SkipNode*[level + 1];
}
~SkipNode(){
delete[] forward;
}
};

跳表的实现

1. 初始化

初始化的过程就是生成下图中红色区域内的部分,也就是跳表的基本结构: 

2. 查找操作

跳表的查找依赖于节点的forward数组,跳表的每个节点都有一个后继节点数组forward, 里面存放着该节点向后的多个层次的后继。后继节点层次越高,则距离当前节点越远。这样在进行查找的时候,先看高层次的后继节点,如果后继节点的key小于要找的key,则沿着当前层次的后继节点链继续走,直到后继节点的key大于等于要查找的key;然后层次减去一继续进行搜索操作,直到层次变为0.

3. 插入操作

插入的时候,先根据key值利用查找操作的方法找到应该被插入的位置,然后修改forward指针,并更新跳表的level变量。 

4. 删除操作

删除的时候,先根据key值找到节点的位置,然后更改forward指针,并更新跳表的level变量。 

实现(c++)

#include<iostream>
using namespace std;
#define MAX_LEVEL 20
#define INVALID_VALUE 1 << 30
struct SkipNode{
int key; //跳表节点的key
int value; //跳表节点的val,可以是其他类型
int level; //跳表节点的level,实际的节点level从0 开始,计到level
SkipNode** forward; //跳表节点的后继 SkipNode(int lvl){ //跳表节点的level,则它的level计数从0计到level
level = lvl;
forward = new SkipNode*[level + 1];
}
~SkipNode(){
delete[] forward;
}
}; struct SkipList{
SkipNode* header;
SkipList(){
header = new SkipNode(0); //初始时,只设置一层跳
for (int i = 0; i < MAX_LEVEL; i++){
header->forward[i] = NULL;
}
};
~SkipList(){ }
int FindEle(int key){
SkipNode* p = header, *q;
int k = header->level;
//跳表的每个节点多层,则含有多个后继节点,后继节点的level越高,则该后继节点距离该节点越远
//在查找的时候,将一个节点的后继节点level从高到低进行遍历。
//当前节点在level层时,其后继节点的key若大于目标节点,则应该缩小后继距离,则此时可以减小level...直到level为0.
//到level为0时候节点q的key不小于 要查找的key,则若跳表中存在元素key,则应该为q节点
for (int level = k; level >= 0; level--){
q = p->forward[level];
while (q && q->key < key){
p = q;
q = p->forward[level];
};
}
if (q->key == key){
return q->value;
}
return INVALID_VALUE;
}
bool Insert(int key, int val){
SkipNode* p = header, *q;
SkipNode* update[MAX_LEVEL]; //update数组记录每层 最后一个节点key值小于要插入的key的节点
int k = header->level; // k 记录跳表中level的最大值
for (int level = k; level >= 0; level--){
q = p->forward[level];
while (q && q->key < key){
p = q;
q = p->forward[level];
}
update[level] = p;
}
if (q->key == key){ //不能含有相同的元素
q->value = val;
return false;
}
int new_level = rand();
if (new_level > header->level){ //随机生成节点的level
if (new_level > header->level && header->level == MAX_LEVEL - 1){
new_level = header->level;
}
else{
k = new_level = ++header->level; //若生成的level大于header的level,则更新 header->level 和 k
update[k] = header;
} }
SkipNode* new_node = new SkipNode(new_level);
new_node->key = key;
new_node->value = val; //对插入的节点,更改其前驱和后继
for (int level = k; level >= 0; level--){
p = update[level];
new_node->forward[level] = p->forward[level];
p->forward[level] = new_node;
}
return true;
} bool Delete(int key){
SkipNode* p = header, *q;
SkipNode* update[MAX_LEVEL]; //update数组记录每层 最后一个节点key值小于要插入的key的节点
int k = header->level; // k 记录跳表中level的最大值
for (int level = k; level >= 0; level--){
q = p->forward[level];
while (q && q->key < key){
p = q;
q = p->forward[level];
}
update[level] = p;
}
if (!q || q->key != key){ //若不存在元素key,直接返回
return false;
}
//删除该节点,并更新其前驱和后继
for (int level = k; level >= 0; level--){
p = update[level];
if (p->forward[level] == q){ //如果update节点的后继为要删除的节点,则进行更新
p->forward[level] = q->forward[level];
}
}
//删除该节点,可能会导致整个跳表的level减少
while (header->forward[k] == NULL && k >= 0)
k--;
header->level = k; delete q; return true;
}
};

参考: 跳表SkipList

跳表 SkipList的更多相关文章

  1. 跳表SkipList

    原文:http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html 跳表SkipList   1.聊一聊跳表作者的其人其事 2. 言归正 ...

  2. 存储系统的基本数据结构之一: 跳表 (SkipList)

    在接下来的系列文章中,我们将介绍一系列应用于存储以及IO子系统的数据结构.这些数据结构相互关联又有着巨大的区别,希望我们能够不辱使命的将他们分门别类的介绍清楚.本文为第一节,介绍一个简单而又有用的数据 ...

  3. 3.3.7 跳表 SkipList

    一.前言 concurrentHashMap与ConcurrentSkipListMap性能测试 在4线程1.6万数据的条件下,ConcurrentHashMap 存取速度是ConcurrentSki ...

  4. 跳表(SkipList)设计与实现(Java)

    微信搜一搜「bigsai」关注这个有趣的程序员 文章已收录在 我的Github bigsai-algorithm 欢迎star 前言 跳表是面试常问的一种数据结构,它在很多中间件和语言中得到应用,我们 ...

  5. [转载] 跳表SkipList

    原文: http://www.cnblogs.com/xuqiang/archive/2011/05/22/2053516.html leveldb中memtable的思想本质上是一个skiplist ...

  6. 跳表(skiplist)Python实现

    # coding=utf-8 # 跳表的Python实现 import random # 最高层数设置为4 MAX_LEVEL = 4 def randomLevel(): ""& ...

  7. C语言跳表(skiplist)实现

    一.简介 跳表(skiplist)是一个非常优秀的数据结构,实现简单,插入.删除.查找的复杂度均为O(logN).LevelDB的核心数据结构是用跳表实现的,redis的sorted set数据结构也 ...

  8. 跳表(SkipList)原理篇

    1.什么是跳表? 维基百科:跳表是一种数据结构.它使得包含n个元素的有序序列的查找和插入操作的平均时间复杂度都是 O(logn),优于数组的 O(n)复杂度.快速的查询效果是通过维护一个多层次的链表实 ...

  9. redis笔记_源码_跳表skiplist

    参照:https://juejin.im/post/57fa935b0e3dd90057c50fbc#comment http://redisbook.com/preview/skiplist/dat ...

随机推荐

  1. C语言 · 龟兔赛跑预测

    基础练习 龟兔赛跑预测   时间限制:1.0s   内存限制:512.0MB        锦囊1 模拟.   问题描述 话说这个世界上有各种各样的兔子和乌龟,但是研究发现,所有的兔子和乌龟都有一个共 ...

  2. pthread_join直接决定资源是否能够及时释放

    /*http://hankjin.blog.163.com/blog/static/33731937201072675024100/ pthread的内存泄露 # cc thread.c -lpthr ...

  3. 用STS创建Maven的Web项目<转>

    右键New——>other——>Maven——>Maven Project 弹出框中点击Next,在Filter中写上:webapp. 然后在下面的框中选择org.apache.ma ...

  4. java资料——线性表(转)

    线性表 线性表(亦作顺序表)是最基本.最简单.也是最常用的一种数据结构.线性表中数据元素之间的关系是一对一的关系,即除了第一个和最后一个数据元素之外,其它数据元素都是首尾相接的.线性表的逻辑结构简单, ...

  5. 一站式学习Wireshark(四):网络性能排查之TCP重传与重复ACK

    作为网络管理员,很多时间必然会耗费在修复慢速服务器和其他终端.但用户感到网络运行缓慢并不意味着就是网络问题. 解决网络性能问题,首先从TCP错误恢复功能(TCP重传与重复ACK)和流控功能说起.之后阐 ...

  6. KBEngine.executeRawDatabaseCommand使用

    先贴一段官方的API介绍: def executeRawDatabaseCommand( command, callback, threadID, dbInterfaceName ): 功能说明: 这 ...

  7. tensorflow学习笔记(10) mnist格式数据转换为TFrecords

    本程序 (1)mnist的图片转换成TFrecords格式 (2) 读取TFrecords格式 # coding:utf-8 # 将MNIST输入数据转化为TFRecord的格式 # http://b ...

  8. PHP高手修炼50法——勤快篇

    .把PHP当成一门新的语言学习: .看<PHP与mysql5?web?开发技术详解>和<PHP高级程序设计:模式.框架与测试>: .不要被VC.BCB.BC.MC.TC等词汇所 ...

  9. 在jsp中,page指令的()属性用来引入需要的包或类。

    在jsp中,page指令的()属性用来引入需要的包或类. A.extends B.import C.language D.contentType 解答:B

  10. Unity3D - 详解Quaternion类(一)

    一.简介 Quaternion又称四元数,由x,y,z和w这四个分量组成,是由爱尔兰数学家威廉·卢云·哈密顿在1843年发现的数学概念.四元数的乘法不符合交换律.从明确地角度而言,四元数是复数的不可交 ...