跳表skiplist的理解

758 阅读4分钟

跳表的实现代码参考

/*
 * create data: 2018/03/25 23:20
 * desc: skip_list imp
 * refer to: https://blog.csdn.net/daniel_ustc/article/details/20218489
 */

#include <iostream>
#include <stdlib.h>
#include <time.h>
using namespace std;

#define MAX_LEVEL 8

// 跳表节点定义
typedef struct node
{
    int key;
    int value;
    struct node* next[1]; // 后继指针数组,柔性数组,可实现结构体的变长
} Node;

// 跳表定义
typedef struct skip_list
{
    int level; // 层数
    Node* head;
} skip_list;


// 通过下述宏定义实现包含可变层数(n)的Node*类型的内存空间
#define new_node(n) ((Node*)malloc(sizeof(struct node) + n * sizeof(Node*)))

// 创建跳表节点
Node* create_node(int level, int key, int value)
{
    Node* p = new_node(level);
    if (!p) {
        return NULL;
    }
    p->key = key;
    p->value = value;
    return p;
}

// 创建跳表
skip_list* create_skip_list()
{
    skip_list* sl = (skip_list*)malloc(sizeof(struct skip_list));
    if (NULL == sl) {
        return NULL;
    }
    sl->level = 0; // 设置跳表的层,初始为0层,即没有任何元素,随着链表的扩充,该值会慢慢增大
    Node* tmp = create_node(MAX_LEVEL, 0, 0); // 创建头节点
    if (NULL == tmp) {
        free(sl);
        return NULL;
    }
    sl->head = tmp;

    // 初始情况下,next数组都指向NULL
    for (int i = 0; i < MAX_LEVEL; i++) {
        tmp->next[i] = NULL;
    }
    srand(time(NULL));
    return sl;
}

// 插入元素
// 插入的时候元素所占有的层数完全是随机算法
int randomLevel()
{
    int level = 1;
    while(rand() % 2) {
        level++;
    }

    cout << "rand level: " << level << endl;
    level = (MAX_LEVEL > level ? level : MAX_LEVEL);
    return level;
}

bool insert(skip_list* sl, int key, int value)
{
    Node* update[MAX_LEVEL];
    Node* q = NULL, *p = sl->head;
    int i = sl->level-1;

    // 从最高层往下查找需要插入的位置,并更新update数组
    // 具体是指:新插入的节点所占有的层数完全是随机的,层数是通过随机算法产生的,因此可能大于当前跳表的层数,也可能
    // 小于或等于,这里暂且不管,先按照当前跳表的层数计算在每一个层应该插入的位置(从高层往底层依次计算)。
    // 如果netx[i]为null或者next[i]对应的key大于等于要插入的key了,则停止while循环,此时的p的下一个节点即为第i层的待插入位置。
    // 继续for循环(i--),进入下一层,比如从p->next[4] 进入 p->next[3],从第三层找netx[3]为null或者next[3]对应的key
    // 大于等于要插入的key,这时候再次停止while循环,此时的p的下一个节点为第3层的待插入位置。
    for (; i >= 0; i--) {
        while((q = p->next[i]) && q->key < key) {
            p = q;
        }
        update[i] = p; // 此时p指向的是当前所在层的下一节点为NULL或者下一节点值大于等于要插入的值,因为要插入的节点肯定是在比它大的节点的前面
    }
    if (q && q->key == key) {
        // key已存在
        q->value = value;
        return true;
    }
    int level = randomLevel();
    if (level > sl->level) {
        // 随机的层数有可能会大于当前跳表的层数,那么多余的那部分层数对应的update[i]置为sl->head,后面用来初始化
        for (i = sl->level; i < level; i++) {
            update[i] = sl->head;
        }
        sl->level = level;
    }
    q = create_node(level, key, value);
    if (!q) {
        return false;
    }

    // 逐层更新节点的指针(这里的层指的是随机的层,比如当前有4层,然后随机的层为2,则只会将新节点插入下面的两层)
    // 如果当前跳表层是4,随机的为6,则会把5、6层也赋值,用到update[i] = sl->head;这里的结果。
    for (i = level - 1; i >= 0; i--) {
        // 这里就是说随机几层,就用到update中的那几层,插入到update[i]对应的节点之后
        q->next[i] = update[i]->next[i];
        update[i]->next[i] = q;
    }
    return true;
}

// 删除节点
bool erase(skip_list* sl, int key)
{
    Node* update[MAX_LEVEL];
    Node* q = NULL, *p = sl->head;
    int i = sl->level - 1;
    for (; i >= 0; i--) {
        while((q = p->next[i]) && q->key < key) {
            p = q;
        }
        update[i] = p;
    }
    // 没找到
    if (!q || (q && q->key != key)) {
        return false;
    }
    // 逐层删除与普通链表删除一样
    for (i = sl->level - 1; i >= 0; i--) {
        if (update[i]->next[i] == q) {
            update[i]->next[i] = q->next[i];
            // 如果删除的节点是最高层的节点,则level--
            if (sl->head->next[i] == NULL) {
                sl->level--;
            }
        }
    }
    free(q);
    q = NULL;
    return true;
}

// 因为是从高层往底层查,所以效率会提高很多
int* search(skip_list* sl, int key)
{
    Node* q = NULL, *p = sl->head;
    int i = sl->level - 1;
    for (; i >= 0; i--) {
        while((q = p->next[i]) && q->key < key) {
            p = q;
        }
        if (q && key == q->key) {
            return &(q->value);
        }
    }
    return NULL;
}

void skip_list_destroy(skip_list* sl)
{
    if (!sl) {
        return;
    }

    Node* q = sl->head;
    Node* next = NULL;
    while(q) {
        next = q->next[0];
        free(q);
        q = next;
    }
    free(sl);
}

void print_skip_list(skip_list* sl)
{
    cout << "print the skip_list ......" << endl;
    Node* head = sl->head;
    while(head) {
        cout << head->key << endl;
        head = head->next[0];
    }
}

int main()
{
    skip_list* sl = create_skip_list();
    for (int i = 1; i < 10; i++) {
        insert(sl, i, i);
    }
    // for (int i = 10; i < 50; i++) {
    //     erase(sl, i);
    // }
    print_skip_list(sl);
    int* p = search(sl, 8);
    if (p) {
        cout << "88 found." << endl;
    }
    cout << "the skip_list level: " << sl->level << endl;
    //skip_list_destroy(sl);
    return 0;
}