本来跳表的原理很easy的(相对于红 - 黑树),但国庆间歇性地搞5天才捞起来……

我学会了跳之前写表的链式结构完全基于,我看着写的过程中redis实现,它的每个键列都是用数组来表示的。细致想了想发现这样的实现除了跳表的最大层数会被固定(由于是用的数组)之外,在性能、代码简洁性方面都是很好的。并且实际使用中。可能也并不希望跳表的层数毫无限制地增长。

只是最后我自己的实现还是依照纯粹链式结构实现,由于数组的方式redis已经实现过了。

关于跳表原理网上非常多,这里不再赘述。代码疏漏之处恳请指出。

上一张图表示我代码中的跳表逻辑结构:

跳表API定义——skip_list.h

#ifndef SKIP_LIST_H_INCLUDED
#define SKIP_LIST_H_INCLUDED typedef struct skip_list_s *skip_list_t; /**
* @return 新建的的空跳表实例
*/
skip_list_t
skip_list_create(); /**
* 销毁跳表实例,不会销毁跳表中包括的值。
*/
void
skip_list_destroy(skip_list_t sl); /**
* 查询跳表中key相应的值。
* 返回NULL不代表跳表中一定不包括key。以skip_list_contains(sl, key)结果为准。 * @param key 要查询的键。同意key在跳表中不存在。 * @return 跳表中key相应的值
*/
void*
skip_list_get(skip_list_t sl, int key); /**
* 向跳表中加入一个键值对,这将使得skip_list_contains(sl, key)==1。
* 假设跳表中已经存在同样的键,则替换其旧值,否则创建一个新的键值对。
* @param value key相应的新的值,同意为NULL。 * @return 跳表中key原来相应的值
*/
void*
skip_list_put(skip_list_t sl, int key, void *value); /**
* 从跳表中删除一个键值对,这将使得skip_list_contains(sl, key)==0。
* @param key 要删除的键,同意key在跳表中不存在。
* @return 跳表中key相应的值
*/
void*
skip_list_remove(skip_list_t sl, int key); /**
* @return 跳表中存在key则1,否则0
*/
int
skip_list_contains(skip_list_t sl, int key); /**
* @return 跳表中键的数量
*/
int
skip_list_count(skip_list_t sl); /**
* 检索跳表中键的集合。结果依照键升序排列
* @param [out] keys 用于存储键集合
* @param [int] length keys数组的长度
* @return 键的数量(=MIN(length, 跳表中全部键的数量))
*/
int
skip_list_key_set(skip_list_t sl, int keys[], int length); /**
* 检索跳表中值的集合,结果依照键升序排列
* @param [out] values 用于存储值集合
* @param [int] length values数组的长度
* @return 值的数量(=MIN(length, 跳表中全部键的数量))
*/
int
skip_list_value_set(skip_list_t sl, void *values[], int length); #endif // SKIP_LIST_H_INCLUDED

跳表API測试——main.c

#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "skip_list.h" #define COUNT 10 int main() {
skip_list_t sl;
int i, tmp, *keys; keys = (int*)malloc(COUNT*sizeof(int)); srand(time(NULL)); sl = skip_list_create(); for(i=0; i<COUNT; i++) {
keys[i] = rand();
tmp = rand();
printf("put %5d : %5d, return %5d", keys[i], tmp, (int)skip_list_put(sl, keys[i], (void*)tmp));
printf(", count=%d\n", skip_list_count(sl));
} puts("*****************************************"); for(i=0; i<COUNT; i++) {
printf("put %5d : %5d, return %d\n", keys[i], keys[i], (int)skip_list_put(sl, keys[i], (void*)keys[i]));
} puts("*****************************************"); skip_list_key_set(sl, keys, COUNT);
printf("key set : ");
for(i=0; i<COUNT-1; i++) {
printf("%d, ", keys[i]);
}
printf("%d\n", keys[COUNT-1]); puts("*****************************************"); for(i=0; i<COUNT; i++) {
printf("get %5d, return %d\n", keys[i], (int)skip_list_get(sl, keys[i]));
} puts("*****************************************"); for(i=0; i<COUNT; i++) {
printf("constains %5d, return %d\n", keys[i], skip_list_contains(sl, keys[i]));
} puts("*****************************************"); for(i=0; i<COUNT; i++) {
printf("remove %5d, return %5d", keys[i], (int)skip_list_remove(sl, keys[i]));
printf(", count=%d\n", skip_list_count(sl));
} puts("*****************************************"); for(i=0; i<COUNT; i++) {
printf("constains %5d, %d\n", keys[i], skip_list_contains(sl, keys[i]));
} skip_list_destroy(sl); free(keys); return 0;
}

跳表API实现——skip_list.c

#include "skip_list.h"
#include <stdlib.h> typedef struct data_s *data_t; typedef struct node_s *node_t; //表示节点中存储的键值对
struct data_s {
int key;
void *value;
}; //表示跳表中的节点
struct node_s {
node_t right;
node_t down;
data_t data; //注意同一列的全部节点都指向同一个data
}; //依照二叉查找树的概率分布随机生成一个节点高度
static inline int
rand_level() {
int level = 1;
while(rand()&1) {
level++;
}
return level;
} //从node右边開始逐层向下查找key相应的键值对
//在某一层找到以后马上返回,以提高查找速度
//node不能为NULL
static inline data_t
search_data(node_t node, int key) {
for(; node; node = node->down) {
for(; node->right && key > node->right->data->key; node = node->right);
//此时node->data->key < key <= node->right->data->key
if(node->right && key == node->right->data->key) {
return node->right->data;
}
}
return NULL;
} //从node右边開始逐层向下查找key相应的键值对,并将垂直路径记录在upadte数组中
//必须走到最底层以后才返回,以便记录完整的update路径
//node和update不能为NULL
static inline data_t
search_data_update(node_t node, int key, node_t *update) {
for(;; node = node->down) {
for(; node->right && key > node->right->data->key; node = node->right);
//node->data->key < key <= node->right->data->key
//保证当前node一定在目标key的左边。以便remove时更新
*update++ = node;
if(!node->down) {
break;
}
}
if(node->right && key == node->right->data->key) {
return node->right->data;
}
return NULL;
} //在跳表最顶层上面添加一些空层
//top_left不能为NULL,性能能够改进
static inline int
gain_empty_top_lines(node_t top_left, int count) {
int i;
for(i = 0; i < count; i++) {
node_t tmp;
tmp = (node_t)malloc(sizeof(struct node_s));
tmp->right = top_left->right;
tmp->down = top_left->down;
top_left->right = NULL;
top_left->down = tmp;
}
return i;
} //清除跳表最顶层的几个空层
//top_left不能为NULL。性能能够改进
static inline int
clean_empty_top_lines(node_t top_left) {
int count;
for(count = 0; !top_left->right; count++) {
node_t tmp = top_left->down;
if(!tmp) {
break;
}
top_left->right = tmp->right;
top_left->down = tmp->down;
free(tmp);
}
return count;
} //在跳表中为新的键值对添加一列位置
//data和update不能为NULL
static inline void
add_key_column(data_t data, node_t *update, int length) {
int i;
for(i=0; i<length; i++) {
node_t tmp;
tmp = (node_t)malloc(sizeof(struct node_s));
tmp->data = data;
tmp->right = update[i]->right;
update[i]->right = tmp;
}
for(i=0; i<length-1; i++) {
update[i]->right->down = update[i+1]->right;
}
update[length-1]->right->down = NULL;
} //在跳表中删除key所在的列
//update不能为NULL
static inline void
remove_key_column(int key, node_t *update, int length) {
int i;
for(i = 0; i < length; i++) {
node_t right = update[i]->right;
if(right && right->data->key == key) {
update[i]->right = right->right;
free(right);
}
}
} //释放节点并返回它的下一个(右边或下边)节点
static inline node_t
free_and_next(node_t node, node_t next) {
free(node);
return next;
} struct skip_list_s {
struct node_s top_left; //跳表左上角的节点
int level; //跳表层数
int count; //跳表中键值对的数量
}; skip_list_t
skip_list_create() {
skip_list_t sl;
sl = (skip_list_t)malloc(sizeof(struct skip_list_s));
sl->top_left.right = NULL;
sl->top_left.down = NULL;
sl->level = 1;
sl->count = 0;
return sl;
} void
skip_list_destroy(skip_list_t sl) {
node_t left, node;
for(left = &sl->top_left; left->down; left = left->down) {
for(node = left->right; node; node = free_and_next(node, node->right));
}
for(node = left->right; node; node = free_and_next(node, node->right));
for(left = sl->top_left.down; left; left = free_and_next(left, left->down));
free(sl);
} void*
skip_list_get(skip_list_t sl, int key) {
data_t data;
data = search_data(&sl->top_left, key);
if(data) {
return data->value;
}
return NULL;
} void*
skip_list_put(skip_list_t sl, int key, void *value) {
void *old_value = NULL;
data_t data;
data = search_data(&sl->top_left, key);
if(data) {
old_value = data->value;
data->value = value;
} else {
node_t *update;
int target_level;
target_level = rand_level();
if(target_level > sl->level) {
sl->level += gain_empty_top_lines(&sl->top_left, target_level-sl->level);
}
update = (node_t*)malloc(sizeof(node_t)*sl->level);
search_data_update(&sl->top_left, key, update);
data = (data_t)malloc(sizeof(struct data_s));
data->key = key;
data->value = value;
//target_level<=sl->level
add_key_column(data, update+(sl->level-target_level), target_level);
free(update);
sl->count++;
}
return old_value;
} void*
skip_list_remove(skip_list_t sl, int key) {
void *old_value = NULL;
node_t *update;
data_t data;
update = (node_t*)malloc(sizeof(node_t)*sl->level);
data = search_data_update(&sl->top_left, key, update);
if(data) {
//删除key所在列
remove_key_column(key, update, sl->level);
//清除掉删除key所在列以后上面出现的空行
sl->level -= clean_empty_top_lines(&sl->top_left);
old_value = data->value;
free(data);
sl->count--;
}
free(update);
return old_value;
} int
skip_list_contains(skip_list_t sl, int key) {
return !!search_data(&sl->top_left, key);
} int
skip_list_count(skip_list_t sl) {
return sl->count;
} int
skip_list_key_set(skip_list_t sl, int keys[], int length) {
int i;
node_t left, node;
for(left = &sl->top_left; left->down; left = left->down);
for(i = 0, node = left->right; i<length && node; i++, node = node->right) {
keys[i] = node->data->key;
}
return i;
} int
skip_list_value_set(skip_list_t sl, void *values[], int length) {
int i;
node_t left, node;
for(left = &sl->top_left; left->down; left = left->down);
for(i = 0, node = left->right; i<length && node; i++, node = node->right) {
values[i] = node->data->value;
}
return i;
}

版权声明:本文博主原创文章,博客,未经同意不得转载。

跳转表C语言,不比redis版本号的更多相关文章

  1. oracle exp imp 导入 正在跳过表 plsql 导入表 成功终止 数据 被导入

    http://blog.csdn.net/agileclipse/article/details/12968011 .导入过程中,所有表导入都出现提示, 正在跳过表...某某表名 最后提示成功终止导入 ...

  2. Java语言访问Redis数据库之Set篇

    如果想通过Java语言对Redis数据库进行访问. 首先,需要安装Redis数据库,可以是Windows系统,或者Linux系统.(本文以Windows系统的本地Redis数据库为例,代码说明如何操作 ...

  3. 数据结构与算法之顺序表C语言实现

    顺序表等相关概念请自行查阅资料,这里主要是实现. 注: 1.顺序表C语言实现: 2.按较简单的方式实现,主要帮助理解,可在此基础上修改,更加完善: 3.提供几个简单函数,可自行添加功能: 4.可用C+ ...

  4. 词典(一) 跳转表(Skip table)

    词典,顾名思义,就是通过关键码来查询的结构.二叉搜索树也可以作为词典,不过各种BST,如AVL树.B-树.红黑树.伸展树,结构和操作比较复杂,而且理论上插入和删除都需要O(logn)的复杂度. 在词典 ...

  5. Go语言操作Redis

    Go语言操作Redis Redis介绍 Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据结构,很多业务场景下的问题都可以很自然地映射到这些数据结构上.除此之外,通过复制.持久化和客 ...

  6. GO学习-(24) Go语言操作Redis

    Go语言操作Redis 在项目开发中redis的使用也比较频繁,本文介绍了Go语言中go-redis库的基本使用. Redis介绍 Redis是一个开源的内存数据库,Redis提供了多种不同类型的数据 ...

  7. go语言使用redis —— redigo

    redis的client有好多好多,go语言的client在redis官方有两个推荐,radix和redigo.选择哪一个好呢?确实很纠结,后来掷硬币决定选择redigo了. redis.go.red ...

  8. 将mysql表数据批量导入redis zset结构中

    工作中有这样一个需求,要将用户的魅力值数据做排行,生成榜单展示前40名,每隔5分钟刷新一次榜单.这样的需求用redis的zset是很方便实现的.但是数据存在mysql的表中,有400多万条,怎么将其快 ...

  9. Codeforces A. Password(KMP的nxt跳转表)

    题目描述: Password time limit per test 2 seconds memory limit per test 256 megabytes input standard inpu ...

随机推荐

  1. (适合入门)JVM堆内存相关的启动参数:年轻一代、岁和永久代内存分配

    假设你要观察JVM进程消耗的堆内存,通过命令工具jmap或可视化工具jvisualvm.exe.JVM这些参数的默认启动值.假设你想知道JVM内存分配策略,最开始手动设置这些参数.通过JDK统计结果, ...

  2. HDU 4790 Just Random 数学

    链接:pid=4790">http://acm.hdu.edu.cn/showproblem.php?pid=4790 意:从[a.b]中随机找出一个数字x,从[c.d]中随机找出一个 ...

  3. Matlab spline

    请记住,,平稳 早期project图时,把富有弹性的细长木条(所谓样条)用压铁固定在样点上,在其它地方让它自由弯曲,然后沿木条画下曲线. 成为样条曲线 三次样条插值(简称Spline插值)是通过一系列 ...

  4. php 禁止 URL 直接访问 php文件

    通过判断访问来源来实现. $fromurl="http://www.111.com/index.php"; //只能从这个地址访问 if( $_SERVER['HTTP_REFER ...

  5. 编译安装LNMP Centos 6.5 x64 + Nginx1.6.0 + PHP5.5.13 + Mysql5.6.19

    (来自:http://www.cnblogs.com/vicowong/archive/2011/12/01/2116212.html) 环境: 系统硬件:vmware vsphere (CPU:2* ...

  6. POJ 1276  Cash Machine(多重背包)

    Cash Machine Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 24132 Accepted: 8446 Descrip ...

  7. Android 纯代码加入点击效果

    项目中非常多的Button, 同一时候配置非常多button切图,Selector是不是非常烦, 使用以下这个类,就能够直接为Button添加点击效果. 不用多个图片,不用Selector. 使用方法 ...

  8. LeetCode18:4Sum

    Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = tar ...

  9. 面试题 收集请求k千里马

    收集请求k最大值 个人信息:就读于燕大本科软件project专业 眼下大三; 本人博客:google搜索"cqs_2012"就可以; 个人爱好:酷爱数据结构和算法,希望将来从事算法 ...

  10. QQ旅程server分析01-网关server

    网关server网络进程: s1.连接网关管理器 s2.上报自身信息给网关管理器 s3.从网关管理器获取自己的管理信息 *s4.依照管理信息预分配好须要的资源 s5.网关server打开client监 ...