跳跃表:
跳跃表与链表结构相似,只是引入“分层”的概念,从上到下的每一层都是一个链表。
借个图:
从图中可观察到跳跃表有以下的性质:
1.每个节点有多个层,每层都有一个指向同层的下一节点的指针
2.每层的链表都是一个有序链表,根据给定的key排序
3.最底层也就是第一层,的链表包含所有节点
4.存在于 k 层的节点,同样也存在于 <k 层的链表中
引入多层的链表的概念是为了加快查找效率,使其从最高层向下查找过程接近于二分查找
想到平衡树的结构,我们期待第 k 层链表的节点个数为 n ,第 k + 1 层的节点个数为 n / 2,且能够均匀分布
像这样:
保证上层节点的数量是下层节点数量的1/2的方法就是,让节点的层数增加的概率为1/2。
int rand_level(){
int level = 1;
while(rand() & 1) //每次节点层数的增加的概率都是 1/2
++level;
return level;
}
但是若想让节点分布均匀,就要支付向平衡树旋转调整的代价,所以只是通过上面的层数计算方法,近似的使节点分布均匀
跳跃表结构
struct skip_node
{
skip_node* level; //层数 随机生成
int score; //分值 就是排序用的key
void* data; //保存的数据
};
struct skip_list
{
skip_node* head; //头结点,
int maxLevel; //整个跳跃表,最大的层数
int length; //跳跃表元素的个数
};
redis中使用跳跃表来保存有序集合。但是redis中的跳跃表结构有所不同,其目的也是针对特殊的需求,而增加了几个字段。
下面是我实现的跳跃表 c语言版:
其中在skip_node中增加了levelNum和backward字段是为了方便操作,方便范围查找和删除。
并且在skip_list中增加了tail节点指针,指向最后一个节点。
#ifndef _SKIP_LIST_H
#define _SKIP_LIST_H
#include <iostream>
#include <string>
#include <ctime>
#include <cstdio>
#include <cstdlib>
using namespace std;
#define SKIP_LIST_MAX_LEVEL 32
struct skip_node;
struct skip_level
{
skip_node* forward; //前进指针
};
struct skip_node
{
skip_level* level; //保存多个层
skip_node* backward; //后退指针后退步数为1
int levelnum; //层数
double score; //分值,作为key对跳跃表排序
void* data; //数据对象
};
struct skip_list
{
skip_node* head; //表头拥有最大层数,不保存数据
skip_node* tail; //指向尾节点
int level; //除表头之外的最大层数
int length; //跳跃表的长度,也就表示节点个数
};
//函数声明
skip_list* create_skiplist();
int rand_level();
skip_node* find_skipnode(skip_list* skiplist, double score);
void find_skipnode_inrange(skip_list* skiplist, double startscore, double endscore, skip_node** startnode, skip_node** endnode);
void insert_skipnode(skip_list* skiplist, double score, void* data);
void remove_skipnode(skip_list* skiplist, skip_node* del);
void remove_inrange(skip_list* skiplist, double startscore, double endscore);
skip_node* find_inrange_first(skip_list* skiplist, double startscore, double endscore);
skip_node* find_inrange_last(skip_list* skiplist, double startscore, double endscore);
void print_skiplist(skip_list* skiplist);
void destroy_skiplist(skip_list* skiplist);
//函数实现
skip_list* create_skiplist() {
skip_list* skiplist = (skip_list*)malloc(sizeof(skip_list));
if (skiplist == NULL)
return NULL;
if ((skiplist->head = (skip_node*)malloc(sizeof(skip_node))) == NULL) {
free(skiplist);
return NULL;
}
skiplist->length = 0;
skiplist->level = 0;
skiplist->head->level = (skip_level*)malloc(sizeof(skip_level) * SKIP_LIST_MAX_LEVEL);
if (skiplist->head->level == NULL) {
free(skiplist->head);
free(skiplist);
return NULL;
}
memset(skiplist->head->level, 0, sizeof(skip_level) * SKIP_LIST_MAX_LEVEL);
skiplist->head->backward = NULL;
skiplist->head->score = 0;
skiplist->head->data = NULL;
skiplist->head->levelnum = SKIP_LIST_MAX_LEVEL;
skiplist->tail = NULL;
srand(time(NULL)); //定义随机数种子,后面会使用rand随机生成节点的层数
return skiplist;
}
int rand_level() {
int level = 1;
while (rand() % 2) {
++level;
}
return level;
}
void insert_skipnode(skip_list* skiplist, double score, void* data) {
skip_node* node = (skip_node*)malloc(sizeof(skip_node));
int level = rand_level();
node->level = (skip_level*)malloc(sizeof(skip_level) * level);
memset(node->level, 0, sizeof(skip_level) * level);
node->score = score;
node->data = data;
node->levelnum = level;
skip_node* update[SKIP_LIST_MAX_LEVEL]; //保存要插入节点的前面的各层指针
memset(update, 0, sizeof(skip_node*) * SKIP_LIST_MAX_LEVEL);
skip_node* start = skiplist->head;
int maxlevel = skiplist->level;
for (int k = maxlevel - 1; k >= 0; k--) { //从高层向下查找
skip_node* end = start;
skip_node* ptmp = NULL;
//找到比score大的节点
while ((ptmp = end->level[k].forward) && ptmp->score < score) {
end = ptmp;
}
update[k] = end;
}
if (level > maxlevel) { //如果新节点的层数大于当前最大层,则它多出来的几层的前指针为head的各层指针
for (int k = level - 1; k >= maxlevel; k--) {
update[k] = skiplist->head;
}
skiplist->level = level;
}
for (int i = 0; i < level; i++) {
node->level[i].forward = update[i]->level[i].forward;
update[i]->level[i].forward = node;
if (i == 0) {
node->backward = update[i];
}
}
if (skiplist->tail == NULL || node->level[0].forward == NULL) {
skiplist->tail = node;
}
++skiplist->length;
}
skip_node* find_skipnode(skip_list* skiplist, double score) {
skip_node* head = skiplist->head;
int maxlevel = skiplist->level;
skip_node* ptmp = NULL;
for (int k = maxlevel - 1; k >= 0; k--) {
while ((ptmp = head->level[k].forward) && ptmp->score < score) {
head = ptmp;
}
if (ptmp && ptmp->score == score) {
return ptmp;
}
}
return NULL;
}
void find_skipnode_inrange(skip_list* skiplist,double startscore, double endscore, skip_node** startnode, skip_node** endnode) {
*startnode = NULL;
*endnode = NULL;
if (startscore > endscore)
return;
int maxlevel = skiplist->level;
skip_node* start = skiplist->head;
skip_node* end = start;
skip_node* ptmp = NULL;
skip_node* ptmp2 = NULL;
bool isFindLeft = false;
bool isFindRight = false;
for (int k = maxlevel - 1; k >= 0; k--) {
while ((ptmp = end->level[k].forward) && !isFindRight && ptmp->score < endscore) {
end = ptmp;
}
if (!isFindRight && ptmp && ptmp->backward && ptmp->backward->score <= endscore) {
*endnode = ptmp;
isFindRight = true;
}
while ((ptmp2 = start->level[k].forward)&& !isFindLeft && ptmp2->score < startscore) {
start = ptmp2;
}
if (!isFindLeft && ptmp2) {
skip_node* x = ptmp2->backward;
if (x && ((x->score < startscore) || x == skiplist->head)) {
*startnode = ptmp2;
isFindLeft = true;
}
}
if (isFindLeft && isFindRight)
break;
}
}
void remove_skipnode(skip_list* skiplist, skip_node* node) {
skip_node* head = skiplist->head;
skip_node* update[SKIP_LIST_MAX_LEVEL];
for (int k = node->levelnum - 1; k >= 0; k--) {
skip_node* x = head;
skip_node* y = NULL;
while ((y = x->level[k].forward) && y != node) {
x = y;
}
update[k] = x;
}
for (int k = node->levelnum - 1; k >= 0; k--) {
update[k]->level[k].forward = node->level[k].forward;
}
if (node->level[0].forward) {
node->level[0].forward->backward = node->backward;
}
free(node);
--skiplist->length;
}
skip_node* find_inrange_first(skip_list* skiplist, double startscore, double endscore) {
skip_node* start = NULL;
skip_node* end = NULL;
find_skipnode_inrange(skiplist, startscore, endscore, &start, &end);
if (start)
return start;
return NULL;
}
skip_node* find_inrange_last(skip_list* skiplist, double startscore, double endscore) {
skip_node* start = NULL;
skip_node* end = NULL;
find_skipnode_inrange(skiplist, startscore, endscore, &start, &end);
if (end)
return end->backward;
return NULL;
}
void remove_inrange(skip_list* skiplist, double startscore, double endscore) {
int maxlevel = skiplist->level;
skip_node* head = skiplist->head;
skip_node* start = NULL;
skip_node* end = NULL;
//找到开始和结束位置,[start,end)
find_skipnode_inrange(skiplist, startscore, endscore, &start, &end);
if (start == NULL)
return;
skip_node* front[SKIP_LIST_MAX_LEVEL];
skip_node* rear[SKIP_LIST_MAX_LEVEL];
memset(front, 0, sizeof(skip_node*) * SKIP_LIST_MAX_LEVEL);
memset(rear, 0, sizeof(skip_node*) * SKIP_LIST_MAX_LEVEL);
//保存范围前面各层的指针
skip_node* ptmp = start->backward;
int ncount = 0;
while (ncount < maxlevel) {
for (; ncount < ptmp->levelnum;) {
front[ncount] = ptmp;
++ncount;
}
ptmp = ptmp->backward;
}
//保存范围后的各层指针
if (end) {
ptmp = end->backward;
ncount = 0;
while (ncount < maxlevel && ptmp != start) {
for (; ncount < ptmp->levelnum;) {
rear[ncount] = ptmp;
++ncount;
}
ptmp = ptmp->backward;
}
}
//将前后连接
for (int i = 0; i < ncount; i++) {
if (rear[i])
front[i]->level[i].forward = rear[i]->level[i].forward;
else
front[i]->level[i].forward = NULL;
}
//删除节点,更改skiplist->length
if(end)
end->backward = start->backward;
int len = 0;
for (; start != end; ) {
skip_node* del = start;
start = start->level[0].forward;
free(del);
++len;
}
skiplist->length -= len;
//更新skiplist->level
for (int k = maxlevel - 1; k >= 0; k--) {
if (head->level[k].forward) {
skiplist->level = k + 1;
break;
}
}
}
void destroy_skiplist(skip_list* skiplist) {
skip_node* head = skiplist->head;
skip_node* del = NULL;
while (head) {
del = head;
head = head->level[0].forward;
free(del->level);
free(del);
}
free(skiplist);
}
void print_skiplist(skip_list* skiplist) {
if (skiplist == NULL)
return;
printf("====================================\n");
skip_node * head = skiplist->head;
while (head->level[0].forward) {
head = head->level[0].forward;
printf("score:%f,level:%d,data:%d\n", head->score, head->levelnum, int(head->data));
}
printf("++++++++++++++++++++++++++++++++++++\n");
}
#endif
测试:
int main() {
skip_list* skip = create_skiplist();
for (int i = 0; i < 10; i++) {
insert_skipnode(skip, (double)i, (void*)(i + 100));
}
print_skiplist(skip);
skip_node* start = NULL;
skip_node* end = NULL;
find_skipnode_inrange(skip, 3, 6.3, &start, &end);
for (; start->score < end->score; start = start->level[0].forward) {
printf("find result score:%f,level:%d,data:%d\n", start->score, start->levelnum, int(start->data));
}
remove_inrange(skip, 3, 6.3);
print_skiplist(skip);
start = find_skipnode(skip, 9);
printf("find result score:%f,level:%d,data:%d\n", start->score, start->levelnum, int(start->data));
insert_skipnode(skip, -3, (void*) 789);
print_skiplist(skip);
insert_skipnode(skip, 54, (void*)999);
print_skiplist(skip);
remove_inrange(skip, 100, 300);
print_skiplist(skip);
remove_inrange(skip, 50, 100);
print_skiplist(skip);
destroy_skiplist(skip);
system("pause");
return 0;
}
结果:
至于跳跃表与红黑树,AVL相比的优势,在查找过程中跳跃表只是近似二分,所以在效率上恐不能取胜,但是从代码的复杂度上看,逻辑比红黑树和AVL,要简单一些,现在想一想红黑树的爷爷,叔叔,兄弟节点,条件太多。