//
|
|
// Created by fox on 2017/10/10.
|
|
// Skip Lists: A Probabilistic Alternative to Balanced Trees
|
|
// indexable skip list
|
|
// duplicated score and node
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <limits.h>
|
|
#include <string.h>
|
|
#include "skiplist.h"
|
|
#include <time.h>
|
|
|
|
// from 0 to n
|
|
#define SKIPLIST_MAX_LEVEL 15
|
|
#define LEVEL_SIZE (SKIPLIST_MAX_LEVEL+1)
|
|
#define RAND_UNIFORM(x) (int)((double)rand() / RAND_MAX * (x))
|
|
|
|
/* Create a skiplist node with the specified number of levels. */
|
|
snode *skiplistCreateNode(int level, int score, vtype value) {
|
|
snode *zn =
|
|
(snode *)icalloc(sizeof(*zn)+level*sizeof(struct skiplistLevel));
|
|
zn->score = score;
|
|
zn->value = value;
|
|
return zn;
|
|
}
|
|
|
|
skiplist *skiplist_init(void)
|
|
{
|
|
time_t t;
|
|
srand((unsigned)(time(&t)));
|
|
|
|
snode *header = skiplistCreateNode(LEVEL_SIZE, INT_MAX, INT_MAX);
|
|
for (int i = 0; i < LEVEL_SIZE; ++i) {
|
|
header->level[i].span = 1;
|
|
}
|
|
|
|
skiplist *list = (skiplist *)imalloc(sizeof(skiplist));
|
|
list->header = header;
|
|
list->level = 0;
|
|
list->size = 0;
|
|
|
|
return list;
|
|
}
|
|
|
|
static int rand_level()
|
|
{
|
|
int level = 0;
|
|
while (rand() < RAND_MAX / 2 && level < SKIPLIST_MAX_LEVEL)
|
|
// while (arc4random() < INT_MAX && level < SKIPLIST_MAX_LEVEL)
|
|
level++;
|
|
return level;
|
|
}
|
|
|
|
// insert score,node into a skiplist, return 0
|
|
int skiplist_insert(skiplist *list, int score, vtype value)
|
|
{
|
|
snode **update = (snode **)imalloc(sizeof(snode*) * (list->level + 1));
|
|
unsigned int *fore_width = (unsigned int *)imalloc(sizeof(unsigned int) * (list->level + 1));
|
|
snode *x = list->header;
|
|
int i;
|
|
for (i = list->level; i >= 0; i--) {
|
|
fore_width[i] = 0;
|
|
while (x->level[i].forward && x->level[i].forward->score <= score) {
|
|
fore_width[i] += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
update[i] = x;
|
|
}
|
|
// assert(x->score <= score);
|
|
|
|
int level = rand_level();
|
|
list->size = list->size+1;
|
|
|
|
x = skiplistCreateNode(level+1, score, value);
|
|
|
|
// the lowest layer is one by one
|
|
x->level[0].forward = update[0]->level[0].forward;
|
|
x->level[0].span = 1;
|
|
update[0]->level[0].forward = x;
|
|
|
|
unsigned int temp_width;
|
|
for (i = 1; i <= (list->level < level ? list->level : level); i++) {
|
|
temp_width = fore_width[i-1] + update[i-1]->level[i-1].span;
|
|
x->level[i].forward = update[i]->level[i].forward;
|
|
x->level[i].span = update[i]->level[i].span + 1 - temp_width;
|
|
update[i]->level[i].forward = x;
|
|
update[i]->level[i].span = temp_width;
|
|
}
|
|
|
|
if (level > list->level) {
|
|
// complete the new level
|
|
temp_width = fore_width[list->level] + update[list->level]->level[list->level].span;
|
|
for (i = list->level+1; i <= level; i++) {
|
|
list->header->level[i].span = temp_width;
|
|
list->header->level[i].forward = x;
|
|
x->level[i].forward = NULL;
|
|
x->level[i].span = list->size + 1 - temp_width;
|
|
}
|
|
list->level = level;
|
|
}
|
|
else {
|
|
// complete the unreached level
|
|
for (; i <= list->level; ++i) {
|
|
update[i]->level[i].span++;
|
|
}
|
|
}
|
|
|
|
ifree(update);
|
|
ifree(fore_width);
|
|
return 0;
|
|
}
|
|
|
|
int skiplist_update(skiplist *list, int score, vtype value, int old_score)
|
|
{
|
|
skiplist_delete(list, old_score, value);
|
|
return skiplist_insert(list, score, value);
|
|
}
|
|
|
|
// search score in skiplist.
|
|
// set the struct with index and first node if exists, otherwise set index 0
|
|
void skiplist_search(skiplist *list, int score, skiplist_search_ret *ret)
|
|
{
|
|
snode *x = list->header;
|
|
int temp_width = 1;
|
|
for (int i = list->level; i >= 0; i--) {
|
|
while (x->level[i].forward && x->level[i].forward->score < score) {
|
|
temp_width += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
}
|
|
x = x->level[0].forward;
|
|
|
|
ret->index = temp_width;
|
|
ret->node = x;
|
|
}
|
|
|
|
// first index of score
|
|
int skiplist_index_of_score(skiplist *list, int score)
|
|
{
|
|
snode *x = list->header;
|
|
int temp_width = 1;
|
|
for (int i = list->level; i >= 0; i--) {
|
|
while (x->level[i].forward && x->level[i].forward->score < score) {
|
|
|
|
temp_width += x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
}
|
|
x = x->level[0].forward;
|
|
|
|
// check if existed score
|
|
if (x && x->score == score) {
|
|
return temp_width;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// search skiplist by index. Return the node if exists, otherwise NULL
|
|
snode *skiplist_at(skiplist *list, int index)
|
|
{
|
|
snode *x = list->header;
|
|
for (int i = list->level; i >= 0; i--) {
|
|
while (x->level[i].forward) {
|
|
if (x->level[i].span == index) {
|
|
return x->level[i].forward;
|
|
}
|
|
if (x->level[i].span < index) {
|
|
index -= x->level[i].span;
|
|
x = x->level[i].forward;
|
|
}
|
|
else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static void skiplist_node_free(snode *x)
|
|
{
|
|
if (x) {
|
|
ifree(x);
|
|
}
|
|
}
|
|
|
|
// delete by score,node. Return 0 if success, 1 if fail.
|
|
int skiplist_delete(skiplist *list, int score, vtype value)
|
|
{
|
|
int i;
|
|
snode **update = (snode **)imalloc(sizeof(snode*) * (list->level + 1));
|
|
snode *x = list->header;
|
|
|
|
// find every level before the specified node
|
|
for (i = list->level; i >= 0; --i) {
|
|
while (1) {
|
|
if (!(x->level[i].forward) || x->level[i].forward->score > score) {
|
|
update[i] = x;
|
|
break;
|
|
}
|
|
|
|
if (x->level[i].forward->score < score) {
|
|
x = x->level[i].forward;
|
|
continue;
|
|
}
|
|
|
|
// find the first node with same score
|
|
int j;
|
|
update[i] = x;
|
|
for (j = i-1; j >= 0; --j) {
|
|
while (x->level[j].forward->score < score)
|
|
x = x->level[j].forward;
|
|
|
|
update[j] = x;
|
|
}
|
|
x = x->level[0].forward;
|
|
snode *x_start_search = x;
|
|
|
|
// find the first node with same score and node
|
|
while (x && x->value != value && x->score == score) {
|
|
x = x->level[0].forward;
|
|
}
|
|
if (x && x->score == score) {
|
|
// now x is the node to find
|
|
// find nodes for every level before the node to find
|
|
if (x == x_start_search) {
|
|
// done
|
|
i = 0;
|
|
break;
|
|
}
|
|
|
|
// j is used to judge if change the x_start_search
|
|
j = 0;
|
|
snode *iter;
|
|
for (; i >= 0; --i)
|
|
{
|
|
if (j) {
|
|
update[i] = x_start_search;
|
|
iter = x_start_search->level[0].forward;
|
|
} else{
|
|
iter = x_start_search;
|
|
}
|
|
|
|
while (iter != x) {
|
|
if (iter == update[i]->level[i].forward) {
|
|
j = 1;
|
|
x_start_search = iter;
|
|
iter = iter->level[0].forward;
|
|
update[i] = update[i]->level[i].forward;
|
|
continue;
|
|
}
|
|
|
|
iter = iter->level[0].forward;
|
|
}
|
|
}
|
|
i = 0;
|
|
break;
|
|
}
|
|
else {
|
|
// not found the node
|
|
ifree(update);
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (x->score != score) {
|
|
// not found the node
|
|
ifree(update);
|
|
return 1;
|
|
}
|
|
|
|
for (i = 0; i <= list->level && update[i]->level[i].forward == x; i++) {
|
|
update[i]->level[i].forward = x->level[i].forward;
|
|
update[i]->level[i].span += x->level[i].span - 1;
|
|
}
|
|
for (; i <= list->level; i++) {
|
|
--(update[i]->level[i].span);
|
|
}
|
|
skiplist_node_free(x);
|
|
|
|
while (list->level > 0 && list->header->level[list->level].forward == NULL)
|
|
list->level--;
|
|
list->size--;
|
|
|
|
ifree(update);
|
|
return 0;
|
|
}
|
|
|
|
// ifree the skiplist
|
|
void skiplist_free(skiplist *list)
|
|
{
|
|
snode *current_node = list->header->level[0].forward;
|
|
while(current_node != NULL) {
|
|
snode *next_node = current_node->level[0].forward;
|
|
skiplist_node_free(current_node);
|
|
current_node = next_node;
|
|
}
|
|
ifree(list);
|
|
}
|
|
|
|
// print the skip list, just for test.
|
|
static void skiplist_dump(skiplist *list)
|
|
{
|
|
int *width = (int *)imalloc(sizeof(int) * (list->level + 1) * list->size);
|
|
memset(width, 0, sizeof(int) * (list->level + 1) * list->size);
|
|
snode **tempn = (snode **)imalloc(sizeof(snode*) * (list->level + 1));
|
|
int i = 0, j;
|
|
snode *x = list->header->level[0].forward;
|
|
|
|
for (j = 0; j <= list->level; ++j) {
|
|
tempn[j] = list->header->level[j].forward;
|
|
}
|
|
|
|
while (tempn[0] != NULL) {
|
|
for (j = 1; j <= list->level; ++j) {
|
|
if (tempn[j] == tempn[0]) {
|
|
width[list->size * j + i] = tempn[j]->level[j].span;
|
|
tempn[j] = tempn[j]->level[j].forward;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
tempn[0] = tempn[0]->level[0].forward;
|
|
++i;
|
|
}
|
|
|
|
for (j = list->level; j > 0; --j) {
|
|
for (i = 0; i < list->size; ++i) {
|
|
if (width[j * list->size + i] == 0)
|
|
printf(" ");
|
|
else
|
|
printf("%d ", width[j * list->size + i]);
|
|
}
|
|
printf("\n");
|
|
}
|
|
while (x != NULL) {
|
|
printf("%d:%d->", x->score, x->value);
|
|
x = x->level[0].forward;
|
|
}
|
|
printf("NIL\n");
|
|
|
|
ifree(width);
|
|
ifree(tempn);
|
|
}
|
|
|
|
void test_skiplist(void) {
|
|
time_t t;
|
|
srand((unsigned)(time(&t)));
|
|
|
|
int arr[][2] = { {3, 1}, {3,2}, {6,6}, {9,9}, {3, 3}, {1, 1}, {4, 4}, {8, 8}, {7, 7}, {5,5}}, i;
|
|
// int arr[] = { 3, 6, 9}, i;
|
|
skiplist_search_ret tempx;
|
|
|
|
skiplist *list = skiplist_init();
|
|
|
|
printf("search empty:--------------------\n");
|
|
skiplist_search(list, 5, &tempx);
|
|
if (tempx.index > 0) {
|
|
printf("error, found not existed item!\n");
|
|
}
|
|
|
|
printf("delete empty:--------------------\n");
|
|
skiplist_delete(list, 5, 2);
|
|
|
|
printf("Insert:--------------------\n");
|
|
for (i = 0; i < sizeof(arr) / sizeof(arr[0]); i++) {
|
|
skiplist_insert(list, arr[i][0], arr[i][1]);
|
|
}
|
|
skiplist_dump(list);
|
|
|
|
printf("search empty:--------------------\n");
|
|
skiplist_search(list, 5, &tempx);
|
|
printf("index = %d\n", tempx.index);
|
|
|
|
printf("Search by index:-----------\n");
|
|
int indexes[] = { 11, 3, 10 };
|
|
|
|
for (i = 0; i < sizeof(indexes) / sizeof(indexes[0]); i++) {
|
|
snode *tempnode = skiplist_at(list, indexes[i]);
|
|
if (tempnode) {
|
|
printf("index = %d, score = %d, value = %d\n", indexes[i], tempnode->score, tempnode->value);
|
|
} else {
|
|
printf("no index = %d\n", indexes[i]);
|
|
}
|
|
}
|
|
|
|
printf("Delete:--------------------\n");
|
|
skiplist_delete(list, 3, 2);
|
|
skiplist_delete(list, 3, 1);
|
|
skiplist_delete(list, 6, 6);
|
|
skiplist_dump(list);
|
|
|
|
clock_t start, finish;
|
|
start = clock();
|
|
for (i = 0; i < 30*1000; ++i) {
|
|
if (rand() < RAND_MAX / 5 * 3) {
|
|
skiplist_insert(list, RAND_UNIFORM(100), RAND_UNIFORM(20));
|
|
}
|
|
else {
|
|
skiplist_delete(list, RAND_UNIFORM(100), RAND_UNIFORM(20));
|
|
}
|
|
}
|
|
finish = clock();
|
|
double duration = (double)(finish - start) / CLOCKS_PER_SEC;
|
|
printf( "%f seconds\n", duration );
|
|
|
|
printf("Search:--------------------\n");
|
|
int keys[] = { 0, 3, 7, 100, 11 };
|
|
|
|
for (i = 0; i < sizeof(keys) / sizeof(keys[0]); i++) {
|
|
printf("index = %d, score = %d\n", skiplist_index_of_score(list, keys[i]), keys[i]);
|
|
}
|
|
|
|
skiplist_free(list);
|
|
};
|