413 lines
9.3 KiB
C
413 lines
9.3 KiB
C
#include <strings.h>
|
|
#include "skiplist.h"
|
|
#include "x.h"
|
|
|
|
sl_node *_sl_create_node(int key, void *data)
|
|
{
|
|
sl_node *x = xmalloc(sizeof(sl_node));
|
|
|
|
x->key = key;
|
|
x->data = data;
|
|
x->next = x->prev = x->below = x->above = NULL;
|
|
|
|
return x;
|
|
}
|
|
|
|
void sl_init(slist *sl)
|
|
{
|
|
sl_node *x = _sl_create_node(KEY_INF_MIN, NULL);
|
|
sl_node *y = _sl_create_node(KEY_INF_MAX, NULL);
|
|
|
|
x->next = y;
|
|
y->prev = x;
|
|
|
|
sl->topleft = x;
|
|
sl->bottomleft = x;
|
|
sl->topright = y;
|
|
sl->bottomright = y;
|
|
|
|
sl->maxlevel = 0;
|
|
sl->len = 2; /* -INF and +INF */
|
|
|
|
sl->stat.srchcnt = 0;
|
|
sl->stat.rmcnt = 0;
|
|
sl->stat.addcnt = 0;
|
|
sl->stat.srchnum = 0;
|
|
sl->stat.rmnum = 0;
|
|
sl->stat.addnum = 0;
|
|
sl->stat.maxlen = sl->len;
|
|
sl->stat.currsrch = 0;
|
|
sl->stat.currrm = 0;
|
|
sl->stat.curradd = 0;
|
|
sl->stat.srchcntmax = 0;
|
|
sl->stat.rmcntmax = 0;
|
|
sl->stat.addcntmax = 0;
|
|
}
|
|
|
|
void _sl_add_level(slist *sl)
|
|
{
|
|
sl_node *x = _sl_create_node(KEY_INF_MIN, NULL);
|
|
sl_node *y = _sl_create_node(KEY_INF_MAX, NULL);
|
|
|
|
x->next = y;
|
|
y->prev = x;
|
|
|
|
x->below = sl->topleft;
|
|
y->below = sl->topright;
|
|
sl->topleft->above = x;
|
|
sl->topright->above = y;
|
|
sl->topleft = x;
|
|
sl->topright = y;
|
|
|
|
++sl->maxlevel;
|
|
}
|
|
|
|
void sl_free(slist *sl)
|
|
{
|
|
sl_node *foo;
|
|
sl_node *x;
|
|
sl_node *y;
|
|
|
|
for (foo = sl->bottomleft; foo != NULL;) {
|
|
x = foo;
|
|
foo = foo->next;
|
|
while (x != NULL) {
|
|
y = x;
|
|
x = x->above;
|
|
free(y->data);
|
|
free(y);
|
|
}
|
|
}
|
|
}
|
|
|
|
void _print_key(int key)
|
|
{
|
|
if (key == KEY_INF_MIN) {
|
|
print("-INF -> ");
|
|
} else if (key == KEY_INF_MAX) {
|
|
print("+INF -> ");
|
|
} else {
|
|
print("%d -> ", key);
|
|
}
|
|
}
|
|
|
|
void _sl_println()
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < 79; ++i) {
|
|
print("-");
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
void sl_print(const slist *sl)
|
|
{
|
|
sl_node *foo;
|
|
sl_node *x;
|
|
int level = sl->maxlevel;
|
|
|
|
_sl_println();
|
|
print("from top to bottom:\n");
|
|
for (foo = sl->topleft; foo != NULL; foo = foo->below, --level) {
|
|
print("level %d: ", level);
|
|
for (x = foo; x != NULL; x = x->next) {
|
|
_print_key(x->key);
|
|
}
|
|
print("\n");
|
|
}
|
|
_sl_println();
|
|
print("from below to above:\n");
|
|
for (foo = sl->bottomleft; foo != NULL; foo = foo->next) {
|
|
for (x = foo; x != NULL; x = x->above) {
|
|
_print_key(x->key);
|
|
}
|
|
print("\n");
|
|
}
|
|
_sl_println();
|
|
}
|
|
|
|
#define HEAD 1
|
|
#define TAIL 0
|
|
|
|
/*
|
|
* Repeatedly toss a coin until we get tails.
|
|
* Denote with i the number of times coin came up heads
|
|
* and return it.
|
|
*/
|
|
int _toss_coin(int maxlevel) {
|
|
int i;
|
|
int x;
|
|
|
|
i = 0;
|
|
while(1) {
|
|
x = random() & 0x1;
|
|
if (x == TAIL) {
|
|
break;
|
|
}
|
|
++i;
|
|
/* inc level only by one at one iter */
|
|
if (i == maxlevel + 1) {
|
|
break;
|
|
}
|
|
};
|
|
|
|
return i;
|
|
}
|
|
|
|
/*
|
|
* Remove top levels with only INFINITY nodes until only one such
|
|
* level exists.
|
|
*/
|
|
void _sl_clean_top(slist *sl)
|
|
{
|
|
sl_node *foo;
|
|
|
|
for (foo = sl->topleft; foo->below != NULL;) {
|
|
if (foo->next->key == KEY_INF_MAX
|
|
&& foo->below->next->key == KEY_INF_MAX) {
|
|
sl->topleft = foo->below;
|
|
sl->topright = foo->below->next;
|
|
sl->topleft->above = NULL;
|
|
sl->topright->above = NULL;
|
|
--sl->maxlevel;
|
|
|
|
free(foo->data);
|
|
free(foo->next);
|
|
free(foo);
|
|
|
|
foo = sl->topleft;
|
|
debug("cleaned one level\n");
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Toss coin and get i.
|
|
* If i is greather than or equal to skip list max level then
|
|
* add one more level.
|
|
* Search for x and find the positions of the items with largest key
|
|
* less than x.
|
|
* Insert item into each level up to i.
|
|
* If key is found then just update its data.
|
|
*/
|
|
int sl_add(slist *sl, int key, void *data)
|
|
{
|
|
sl_node *foo;
|
|
sl_node *x;
|
|
int i;
|
|
int k;
|
|
int level;
|
|
sl_node **update;
|
|
int res;
|
|
|
|
if (key == KEY_INF_MIN || key == KEY_INF_MAX) {
|
|
return SL_ADD_INV;
|
|
}
|
|
i = _toss_coin(sl->maxlevel);
|
|
debug("toss coin result: %d\n", i);
|
|
if (i >= sl->maxlevel) {
|
|
/* add one more level */
|
|
_sl_add_level(sl);
|
|
debug("added level\n");
|
|
}
|
|
update = (sl_node **) xmalloc(sizeof(sl_node *) * (sl->maxlevel + 1));
|
|
bzero(update, sizeof(sl_node *) * (sl->maxlevel + 1));
|
|
level = sl->maxlevel;
|
|
|
|
++sl->stat.addnum;
|
|
foo = sl->topleft;
|
|
++sl->stat.addcnt;
|
|
sl->stat.curradd = 1;
|
|
while (1) {
|
|
while (key >= foo->next->key) {
|
|
foo = foo->next;
|
|
++sl->stat.addcnt;
|
|
++sl->stat.curradd;
|
|
}
|
|
if (foo->below == NULL) {
|
|
break;
|
|
}
|
|
/*
|
|
* Save nodes that will be updated with new node.
|
|
*/
|
|
update[level--] = foo;
|
|
foo = foo->below;
|
|
++sl->stat.addcnt;
|
|
++sl->stat.curradd;
|
|
}
|
|
sl->stat.addcntmax = max(sl->stat.addcntmax, sl->stat.curradd);
|
|
update[level] = foo;
|
|
|
|
debug("search finished at key: %d\n", foo->key);
|
|
debug("update list keys:\n");
|
|
for (k = 0; k <= sl->maxlevel; ++k) {
|
|
if (update[k] != 0) {
|
|
debug("%d\n", update[k]->key);
|
|
}
|
|
}
|
|
|
|
debug("adding\n");
|
|
if (foo->key == key) {
|
|
res = SL_ADD_SAME;
|
|
debug("key exists\n");
|
|
do {
|
|
foo->data = data;
|
|
foo = foo->above;
|
|
} while (foo != NULL);
|
|
} else {
|
|
res = SL_ADD_NEW;
|
|
debug("new key\n");
|
|
while (level <= i) {
|
|
x = _sl_create_node(key, data);
|
|
x->next = update[level]->next;
|
|
update[level]->next->prev = x;
|
|
x->prev = update[level];
|
|
update[level]->next = x;
|
|
if (level > 0) {
|
|
x->below = update[level - 1]->next;
|
|
update[level - 1]->next->above = x;
|
|
}
|
|
++level;
|
|
}
|
|
++sl->len;
|
|
if (sl->len > sl->stat.maxlen) {
|
|
sl->stat.maxlen = sl->len;
|
|
}
|
|
}
|
|
_sl_clean_top(sl);
|
|
free(update);
|
|
|
|
return res;
|
|
}
|
|
|
|
/*
|
|
* Start at the top left position.
|
|
* At the current position compare key with node's key.
|
|
* If key is found then return it.
|
|
* If key > node's key then scan forward.
|
|
* If key < node's key then drop down.
|
|
* If we drop down past the bottom list then return NULL.
|
|
*/
|
|
sl_node *sl_find(slist *sl, int key)
|
|
{
|
|
sl_node *foo;
|
|
|
|
foo = sl->topleft;
|
|
sl->stat.currsrch = 1;
|
|
++sl->stat.srchcnt;
|
|
++sl->stat.srchnum;
|
|
while (1) {
|
|
while (key >= foo->next->key) {
|
|
foo = foo->next;
|
|
++sl->stat.srchcnt;
|
|
++sl->stat.currsrch;
|
|
}
|
|
if (foo->below == NULL) {
|
|
break;
|
|
}
|
|
foo = foo->below;
|
|
++sl->stat.srchcnt;
|
|
++sl->stat.currsrch;
|
|
}
|
|
sl->stat.srchcntmax = max(sl->stat.srchcntmax, sl->stat.currsrch);
|
|
|
|
if (foo->key != key) {
|
|
foo = NULL;
|
|
}
|
|
return foo;
|
|
}
|
|
|
|
/*
|
|
* Search for key and find positions with the key.
|
|
* Remove found positions.
|
|
* Remove all but one list containing only INFINITY keys.
|
|
*/
|
|
int sl_remove(slist *sl, int key)
|
|
{
|
|
sl_node *foo;
|
|
sl_node *x;
|
|
int result;
|
|
|
|
foo = sl->topleft;
|
|
sl->stat.currrm = 1;
|
|
++sl->stat.rmcnt;
|
|
++sl->stat.rmnum;
|
|
while (1) {
|
|
while (key >= foo->next->key) {
|
|
foo = foo->next;
|
|
++sl->stat.rmcnt;
|
|
++sl->stat.currrm;
|
|
}
|
|
if (foo->below == NULL) {
|
|
break;
|
|
}
|
|
foo = foo->below;
|
|
++sl->stat.rmcnt;
|
|
++sl->stat.currrm;
|
|
}
|
|
sl->stat.rmcntmax = max(sl->stat.rmcntmax, sl->stat.currrm);
|
|
|
|
if (foo->key == key) {
|
|
result = 1;
|
|
|
|
while (foo != NULL) {
|
|
foo->prev->next = foo->next;
|
|
foo->next->prev = foo->prev;
|
|
free(foo->data);
|
|
x = foo;
|
|
foo = foo->above;
|
|
free(x);
|
|
}
|
|
--sl->len;
|
|
|
|
_sl_clean_top(sl);
|
|
} else {
|
|
result = 0;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void sl_stat_print(const slist *sl)
|
|
{
|
|
size_t avgsrch;
|
|
size_t avgadd;
|
|
size_t avgrm;
|
|
|
|
_sl_println();
|
|
print("skiplist statistics:\n");
|
|
print("current len: %lu\n", sl->len);
|
|
|
|
print("search count: %lu\n", sl->stat.srchcnt);
|
|
print("add count: %lu\n", sl->stat.addcnt);
|
|
print("remove count: %lu\n", sl->stat.rmcnt);
|
|
|
|
print("search num: %lu\n", sl->stat.srchnum);
|
|
print("add num: %lu\n", sl->stat.addnum);
|
|
print("remove num: %lu\n", sl->stat.rmnum);
|
|
|
|
print("current search count: %lu\n", sl->stat.currsrch);
|
|
print("current add count: %lu\n", sl->stat.curradd);
|
|
print("current remove count: %lu\n", sl->stat.currrm);
|
|
|
|
if (sl->stat.srchnum != 0) {
|
|
avgsrch = sl->stat.srchcnt / sl->stat.srchnum;
|
|
print("average search count: %lu\n", avgsrch);
|
|
}
|
|
if (sl->stat.addnum != 0) {
|
|
avgadd = sl->stat.addcnt / sl->stat.addnum;
|
|
print("average add count: %lu\n", avgadd);
|
|
}
|
|
if (sl->stat.rmnum != 0) {
|
|
avgrm = sl->stat.rmcnt / sl->stat.rmnum;
|
|
print("average remove count: %lu\n", avgrm);
|
|
}
|
|
|
|
print("maxlen: %lu\n", sl->stat.maxlen);
|
|
print("max search count: %lu\n", sl->stat.srchcntmax);
|
|
print("max add count: %lu\n", sl->stat.addcntmax);
|
|
print("max remove count: %lu\n", sl->stat.rmcntmax);
|
|
}
|