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

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

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

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

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

跳表API定义——skip_list.h

  1. #ifndef SKIP_LIST_H_INCLUDED
  2. #define SKIP_LIST_H_INCLUDED
  3.  
  4. typedef struct skip_list_s *skip_list_t;
  5.  
  6. /**
  7. * @return 新建的的空跳表实例
  8. */
  9. skip_list_t
  10. skip_list_create();
  11.  
  12. /**
  13. * 销毁跳表实例,不会销毁跳表中包括的值。
  14. */
  15. void
  16. skip_list_destroy(skip_list_t sl);
  17.  
  18. /**
  19. * 查询跳表中key相应的值。
  20. * 返回NULL不代表跳表中一定不包括key。以skip_list_contains(sl, key)结果为准。
  21.  
  22. * @param key 要查询的键。同意key在跳表中不存在。
  23.  
  24. * @return 跳表中key相应的值
  25. */
  26. void*
  27. skip_list_get(skip_list_t sl, int key);
  28.  
  29. /**
  30. * 向跳表中加入一个键值对,这将使得skip_list_contains(sl, key)==1。
  31. * 假设跳表中已经存在同样的键,则替换其旧值,否则创建一个新的键值对。
  32. * @param value key相应的新的值,同意为NULL。
  33.  
  34. * @return 跳表中key原来相应的值
  35. */
  36. void*
  37. skip_list_put(skip_list_t sl, int key, void *value);
  38.  
  39. /**
  40. * 从跳表中删除一个键值对,这将使得skip_list_contains(sl, key)==0。
  41. * @param key 要删除的键,同意key在跳表中不存在。
  42. * @return 跳表中key相应的值
  43. */
  44. void*
  45. skip_list_remove(skip_list_t sl, int key);
  46.  
  47. /**
  48. * @return 跳表中存在key则1,否则0
  49. */
  50. int
  51. skip_list_contains(skip_list_t sl, int key);
  52.  
  53. /**
  54. * @return 跳表中键的数量
  55. */
  56. int
  57. skip_list_count(skip_list_t sl);
  58.  
  59. /**
  60. * 检索跳表中键的集合。结果依照键升序排列
  61. * @param [out] keys 用于存储键集合
  62. * @param [int] length keys数组的长度
  63. * @return 键的数量(=MIN(length, 跳表中全部键的数量))
  64. */
  65. int
  66. skip_list_key_set(skip_list_t sl, int keys[], int length);
  67.  
  68. /**
  69. * 检索跳表中值的集合,结果依照键升序排列
  70. * @param [out] values 用于存储值集合
  71. * @param [int] length values数组的长度
  72. * @return 值的数量(=MIN(length, 跳表中全部键的数量))
  73. */
  74. int
  75. skip_list_value_set(skip_list_t sl, void *values[], int length);
  76.  
  77. #endif // SKIP_LIST_H_INCLUDED

跳表API測试——main.c

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <time.h>
  4. #include "skip_list.h"
  5.  
  6. #define COUNT 10
  7.  
  8. int main() {
  9. skip_list_t sl;
  10. int i, tmp, *keys;
  11.  
  12. keys = (int*)malloc(COUNT*sizeof(int));
  13.  
  14. srand(time(NULL));
  15.  
  16. sl = skip_list_create();
  17.  
  18. for(i=0; i<COUNT; i++) {
  19. keys[i] = rand();
  20. tmp = rand();
  21. printf("put %5d : %5d, return %5d", keys[i], tmp, (int)skip_list_put(sl, keys[i], (void*)tmp));
  22. printf(", count=%d\n", skip_list_count(sl));
  23. }
  24.  
  25. puts("*****************************************");
  26.  
  27. for(i=0; i<COUNT; i++) {
  28. printf("put %5d : %5d, return %d\n", keys[i], keys[i], (int)skip_list_put(sl, keys[i], (void*)keys[i]));
  29. }
  30.  
  31. puts("*****************************************");
  32.  
  33. skip_list_key_set(sl, keys, COUNT);
  34. printf("key set : ");
  35. for(i=0; i<COUNT-1; i++) {
  36. printf("%d, ", keys[i]);
  37. }
  38. printf("%d\n", keys[COUNT-1]);
  39.  
  40. puts("*****************************************");
  41.  
  42. for(i=0; i<COUNT; i++) {
  43. printf("get %5d, return %d\n", keys[i], (int)skip_list_get(sl, keys[i]));
  44. }
  45.  
  46. puts("*****************************************");
  47.  
  48. for(i=0; i<COUNT; i++) {
  49. printf("constains %5d, return %d\n", keys[i], skip_list_contains(sl, keys[i]));
  50. }
  51.  
  52. puts("*****************************************");
  53.  
  54. for(i=0; i<COUNT; i++) {
  55. printf("remove %5d, return %5d", keys[i], (int)skip_list_remove(sl, keys[i]));
  56. printf(", count=%d\n", skip_list_count(sl));
  57. }
  58.  
  59. puts("*****************************************");
  60.  
  61. for(i=0; i<COUNT; i++) {
  62. printf("constains %5d, %d\n", keys[i], skip_list_contains(sl, keys[i]));
  63. }
  64.  
  65. skip_list_destroy(sl);
  66.  
  67. free(keys);
  68.  
  69. return 0;
  70. }

跳表API实现——skip_list.c

  1. #include "skip_list.h"
  2. #include <stdlib.h>
  3.  
  4. typedef struct data_s *data_t;
  5.  
  6. typedef struct node_s *node_t;
  7.  
  8. //表示节点中存储的键值对
  9. struct data_s {
  10. int key;
  11. void *value;
  12. };
  13.  
  14. //表示跳表中的节点
  15. struct node_s {
  16. node_t right;
  17. node_t down;
  18. data_t data; //注意同一列的全部节点都指向同一个data
  19. };
  20.  
  21. //依照二叉查找树的概率分布随机生成一个节点高度
  22. static inline int
  23. rand_level() {
  24. int level = 1;
  25. while(rand()&1) {
  26. level++;
  27. }
  28. return level;
  29. }
  30.  
  31. //从node右边開始逐层向下查找key相应的键值对
  32. //在某一层找到以后马上返回,以提高查找速度
  33. //node不能为NULL
  34. static inline data_t
  35. search_data(node_t node, int key) {
  36. for(; node; node = node->down) {
  37. for(; node->right && key > node->right->data->key; node = node->right);
  38. //此时node->data->key < key <= node->right->data->key
  39. if(node->right && key == node->right->data->key) {
  40. return node->right->data;
  41. }
  42. }
  43. return NULL;
  44. }
  45.  
  46. //从node右边開始逐层向下查找key相应的键值对,并将垂直路径记录在upadte数组中
  47. //必须走到最底层以后才返回,以便记录完整的update路径
  48. //node和update不能为NULL
  49. static inline data_t
  50. search_data_update(node_t node, int key, node_t *update) {
  51. for(;; node = node->down) {
  52. for(; node->right && key > node->right->data->key; node = node->right);
  53. //node->data->key < key <= node->right->data->key
  54. //保证当前node一定在目标key的左边。以便remove时更新
  55. *update++ = node;
  56. if(!node->down) {
  57. break;
  58. }
  59. }
  60. if(node->right && key == node->right->data->key) {
  61. return node->right->data;
  62. }
  63. return NULL;
  64. }
  65.  
  66. //在跳表最顶层上面添加一些空层
  67. //top_left不能为NULL,性能能够改进
  68. static inline int
  69. gain_empty_top_lines(node_t top_left, int count) {
  70. int i;
  71. for(i = 0; i < count; i++) {
  72. node_t tmp;
  73. tmp = (node_t)malloc(sizeof(struct node_s));
  74. tmp->right = top_left->right;
  75. tmp->down = top_left->down;
  76. top_left->right = NULL;
  77. top_left->down = tmp;
  78. }
  79. return i;
  80. }
  81.  
  82. //清除跳表最顶层的几个空层
  83. //top_left不能为NULL。性能能够改进
  84. static inline int
  85. clean_empty_top_lines(node_t top_left) {
  86. int count;
  87. for(count = 0; !top_left->right; count++) {
  88. node_t tmp = top_left->down;
  89. if(!tmp) {
  90. break;
  91. }
  92. top_left->right = tmp->right;
  93. top_left->down = tmp->down;
  94. free(tmp);
  95. }
  96. return count;
  97. }
  98.  
  99. //在跳表中为新的键值对添加一列位置
  100. //data和update不能为NULL
  101. static inline void
  102. add_key_column(data_t data, node_t *update, int length) {
  103. int i;
  104. for(i=0; i<length; i++) {
  105. node_t tmp;
  106. tmp = (node_t)malloc(sizeof(struct node_s));
  107. tmp->data = data;
  108. tmp->right = update[i]->right;
  109. update[i]->right = tmp;
  110. }
  111. for(i=0; i<length-1; i++) {
  112. update[i]->right->down = update[i+1]->right;
  113. }
  114. update[length-1]->right->down = NULL;
  115. }
  116.  
  117. //在跳表中删除key所在的列
  118. //update不能为NULL
  119. static inline void
  120. remove_key_column(int key, node_t *update, int length) {
  121. int i;
  122. for(i = 0; i < length; i++) {
  123. node_t right = update[i]->right;
  124. if(right && right->data->key == key) {
  125. update[i]->right = right->right;
  126. free(right);
  127. }
  128. }
  129. }
  130.  
  131. //释放节点并返回它的下一个(右边或下边)节点
  132. static inline node_t
  133. free_and_next(node_t node, node_t next) {
  134. free(node);
  135. return next;
  136. }
  137.  
  138. struct skip_list_s {
  139. struct node_s top_left; //跳表左上角的节点
  140. int level; //跳表层数
  141. int count; //跳表中键值对的数量
  142. };
  143.  
  144. skip_list_t
  145. skip_list_create() {
  146. skip_list_t sl;
  147. sl = (skip_list_t)malloc(sizeof(struct skip_list_s));
  148. sl->top_left.right = NULL;
  149. sl->top_left.down = NULL;
  150. sl->level = 1;
  151. sl->count = 0;
  152. return sl;
  153. }
  154.  
  155. void
  156. skip_list_destroy(skip_list_t sl) {
  157. node_t left, node;
  158. for(left = &sl->top_left; left->down; left = left->down) {
  159. for(node = left->right; node; node = free_and_next(node, node->right));
  160. }
  161. for(node = left->right; node; node = free_and_next(node, node->right));
  162. for(left = sl->top_left.down; left; left = free_and_next(left, left->down));
  163. free(sl);
  164. }
  165.  
  166. void*
  167. skip_list_get(skip_list_t sl, int key) {
  168. data_t data;
  169. data = search_data(&sl->top_left, key);
  170. if(data) {
  171. return data->value;
  172. }
  173. return NULL;
  174. }
  175.  
  176. void*
  177. skip_list_put(skip_list_t sl, int key, void *value) {
  178. void *old_value = NULL;
  179. data_t data;
  180. data = search_data(&sl->top_left, key);
  181. if(data) {
  182. old_value = data->value;
  183. data->value = value;
  184. } else {
  185. node_t *update;
  186. int target_level;
  187. target_level = rand_level();
  188. if(target_level > sl->level) {
  189. sl->level += gain_empty_top_lines(&sl->top_left, target_level-sl->level);
  190. }
  191. update = (node_t*)malloc(sizeof(node_t)*sl->level);
  192. search_data_update(&sl->top_left, key, update);
  193. data = (data_t)malloc(sizeof(struct data_s));
  194. data->key = key;
  195. data->value = value;
  196. //target_level<=sl->level
  197. add_key_column(data, update+(sl->level-target_level), target_level);
  198. free(update);
  199. sl->count++;
  200. }
  201. return old_value;
  202. }
  203.  
  204. void*
  205. skip_list_remove(skip_list_t sl, int key) {
  206. void *old_value = NULL;
  207. node_t *update;
  208. data_t data;
  209. update = (node_t*)malloc(sizeof(node_t)*sl->level);
  210. data = search_data_update(&sl->top_left, key, update);
  211. if(data) {
  212. //删除key所在列
  213. remove_key_column(key, update, sl->level);
  214. //清除掉删除key所在列以后上面出现的空行
  215. sl->level -= clean_empty_top_lines(&sl->top_left);
  216. old_value = data->value;
  217. free(data);
  218. sl->count--;
  219. }
  220. free(update);
  221. return old_value;
  222. }
  223.  
  224. int
  225. skip_list_contains(skip_list_t sl, int key) {
  226. return !!search_data(&sl->top_left, key);
  227. }
  228.  
  229. int
  230. skip_list_count(skip_list_t sl) {
  231. return sl->count;
  232. }
  233.  
  234. int
  235. skip_list_key_set(skip_list_t sl, int keys[], int length) {
  236. int i;
  237. node_t left, node;
  238. for(left = &sl->top_left; left->down; left = left->down);
  239. for(i = 0, node = left->right; i<length && node; i++, node = node->right) {
  240. keys[i] = node->data->key;
  241. }
  242. return i;
  243. }
  244.  
  245. int
  246. skip_list_value_set(skip_list_t sl, void *values[], int length) {
  247. int i;
  248. node_t left, node;
  249. for(left = &sl->top_left; left->down; left = left->down);
  250. for(i = 0, node = left->right; i<length && node; i++, node = node->right) {
  251. values[i] = node->data->value;
  252. }
  253. return i;
  254. }

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

跳转表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. zoj1610(线段树)

    题目连接:http://acm.zju.edu.cn/onlinejudge/showProblem.do?problemCode=1610 题意:在0-8000长的线段里面,按先后次序依次覆盖颜色, ...

  2. SQL Server 2005使用OSQL连接出错

    错误信息: [SQL Native Client] 命名管道提供程序:无法打开与 Sql Server 的连接[2]. 如下图: 解决方案: 设置Tcp/IP属性,将IP1,IP2,IPALL的TCP ...

  3. 使用C++名单在文档处理和学生成绩管理系统相结合

    对于学生成绩管理系统,我并不陌生,几乎学习C人的语言.做项目会想到学生成绩管理系统,我也不例外.在研究中的一段时间C语言之后,还用C语言到学生管理系统,然后做几个链接.计数,这个系统是以前的系统上的改 ...

  4. SQL Server :理解DCM页

    原文:SQL Server :理解DCM页 我们已经讨论了各种不同的页,包括数据页.GAM与SGAM页.PFS页,还有IAM页.今天我们来看下差异变更页(Differential Change Map ...

  5. 用CasperJs自己主动浏览页面

    CasperJs是一个基于PhantomJs的工具,其比起PhantomJs能够更加方便的进行navigation. 一个最简单的CasperJs代码 创建一个文件baidu.js.用来模拟我们訪问百 ...

  6. BZOJ 3747 POI2015 Kinoman 段树

    标题效果:有m点,每个点都有一个权值.现在我们有这个m为点的长度n该序列,寻求区间,它仅出现一次在正确的点区间内值和最大 想了很久,甚至神标题,奔说是水的问题--我醉了 枚举左点 对于每个请求留点右键 ...

  7. Ewebeditor最新漏洞和漏洞指数

    Ewebeditor最新漏洞和漏洞指数[收集] 来源:转载作者:佚名时间:2009-06-03 00:04:26 下面文章收集转载于网络:) 算是比較全面的ewebeditor编辑器的漏洞收集,如今的 ...

  8. Oracle GoldenGate (以下简称ogg)在异种移植os同一种db之间的数据同步。

    Oracle GoldenGate (以下简称ogg)在异种移植os同一种db之间的数据同步. ogg要实现的功能: 同步可以细化到单个表,满足特定的where条件rows同步,称号column同步. ...

  9. HD2 Tmobile 重新分区代码(使用clk 1.6.5 de)

    fastboot oem part-resize misc: fastboot oem part-resize recovery: fastboot oem part-resize boot: fas ...

  10. C# 6.0 (C# vNext) 的新功能:Exception-Handling Improvements

    于 C# 6.0 包裹在异常处理的新功能,有两个方面的改进: 异步处理(async and await)能力 catch block 总结使用.于 C# 5.0 释放 async and await, ...