本来跳表的原理很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. linux LNMP自动安装脚本

    #!/bin/bashsoft_dir="/home/soft"config_dir="/home/config"httpd="httpd-2.0.5 ...

  2. ubuntu14.04中 gedit 凝视能显示中文,而source insight中显示为乱码的解决的方法

    1.乱码显示情况: watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvcjc3NjgzOTYy/font/5a6L5L2T/fontsize/400/fill/ ...

  3. Macosx Setdns

    通过C语言接口在Mac App内部对系统的DNS配置进行改动. Mac OS X设置DNS代码 演示样例代码setDNS.c内容例如以下: #include <SystemConfigurati ...

  4. MySQL数据转移至MSSQL详解

    一.安装MySQL ODBC驱动 为MySQL安装Connector/ODBC驱动.在此需要注意的一点是Connector/ODBC驱动与MySQL Server的版本对应问题.   二.创建系统DS ...

  5. linux下Oracle11g RAC搭建(九)

    linux下Oracle11g RAC搭建(九) 八.创建ASM仓储 相同在图形化界面操作 [root@node1 ~]#  su - grid [grid@node1 ~]$ asmca   //创 ...

  6. New Hire Training Experience

    Game Description: 1. In a closed door, there will be a circle, and 30 numbers in the circle. 2. Each ...

  7. 世纪互联、微软Azure与无穷小微积分

              今年9月25日,世纪互联正式开通微软Azure商用服务,有感.          我是世纪互联创业历程的见证人(之中的一个),现在看到世纪互联推出微软Azure公有云的商用服务,心 ...

  8. Android中Dialog的使用

    上一篇博文讲到对话框popWindow的使用,这篇博文主要解说Dialog的使用. 1.什么是Dialog? Dialog就是对话框的一种方式! 在Android开发中.我们常常会须要在Android ...

  9. HDU5086Revenge of Segment Tree(数论)

    HDU5086Revenge of Segment Tree(数论) pid=5086" target="_blank" style="">题目 ...

  10. shell程序之逐行读取一文件里的參数且使用此參数每次运行5分钟

    /*********************************************************************  * Author  : Samson  * Date   ...