跳跃链表

跳跃链表的概念

跳跃链表是有序链表的一个变种,在一个有序链表中,查找一个链表中的元素需要进行一次遍历,时间复杂度为O(n),为了加快查找的过程,能够跳过某些元素呢?一个思路就是牺牲一定的空间换时间,对有序链表建立类似索引的结构加快查找过程,跳跃链表基本上就是通过维护一个多层次的链表,每一层链表中的元素是前一层链表元素的子集,搜索时,最开始在最稀疏的层次进行搜索,直至需要查找的元素在该层两个相邻元素中间,这时,将跳转到下一个层次,重复刚才的搜索,直到找到需要查找的元素为止。
在这里插入图片描述

跳跃链表的构建思路

如果给定一个固定的有序链表,构造跳跃链表的话,思路非常简单,就是每一层都取上一层的1/2个元素就可以了(例如,按奇偶,每次都取第偶数个元素作为下一层元素......),这样查找就相当于二分查找的过程,查找一个元素的时间复杂度变为了log(n)。但是实际应用中,有序链表往往不是固定元素个数的,会动态的增删,这种静态的构建过程就不合适了,那该怎么办呢?

可以看到,跳跃链表构建索引的关键是每一层都取上一层的部分元素作为该层的元素,即第i层的元素按某个固定的概率p(例如1/21/4)出现在第i+1层中。可通过这个概率p值对空间和时间进行平衡,p越大,占用空间越多。所以,构建的思路就是在插入一个新的元素时,随机的生成该元素的层数,算法描述如下:

randomLevel()
    lvl := 1
    // random() return a random value in [0..1) 
    while random() < p && lvl < MaxLevel do
        lvl := lvl + 1
    return lvl

即,层次越高,概率越低,但层数也不能太高,太高了无意义,一般根据元素的个数n计算MaxLevel = log(n)作为最高层数。这样,跳跃链表就适应动态变化的情况了。

跳跃链表的插入、查找、删除

查找

查找的思路就是先在最上层查找,找到不到再转向下一层继续查找,这样就可以跳过很多元素,缩小查找范围。算法描述如下:

Search(list, searchKey)
    x := list->header  // loop invariant: x->key < searchKey
    for i := list->level downto 1 do    // downto的意思是从最高层,每次下降一层
        while x->forward[i]->key < searchKey do
            x := x->forward[i]  // x->key < searchKey <= x->forward[1]->key
    x := x->forward[1]
    if x->key = searchKey then return x->value
        else return failure

插入

插入的关键步骤是随机决定节点层数,算法前面已经描述过,与普通链表不同的是跳跃链表是多层的,所以确定层数后每一层都需要插入该元素。
在这里插入图片描述

插入算法描述如下:

Insert(list, searchKey, newValue)
    local update[1.. MaxLevel]
    x := list->leader
    for i:= list->level downto 1 do
        while x->forward[i]->key < searchKey do
            x := x->forward[i]  // x->key < searchKey <= x->forward[1]->key
        update[i] := x  //这里是找出这个元素在每一层的前驱节点
    x := x->forward[1]
    if x->key = searchKey then x->value := newValue
    else
        lvl := randomLevel()
        if lvl > list->level then
            for i := list->level + 1 to lvl do
                update[i] := list->header
            list->level := lvl
        x := makeNode(lvl, searchKey, value)
        for i := 1 to level do  // 这里与普通链表插入节点操作类似,只不过是每层都要插入
            x->forward[i] := update[i]->forward[i]
            update[i]->forward[i] := x 

删除

删除操作与普通链表删除操作类似,不同的地方在于找到该元素后要每一层都做删除操作,算法描述如下:

Delete(list, searchKey)
    local update[1..MaxLevel]
    x := list->header
    for i := list->level downto 1 do
        while x->forward[i]->key < searchKey do
            x := x->forward[i]
        update[i] := x
    x := x->forward[1]
    if x->key = searchKey then
        for i := 1 to list->level do
            if update[i]->forward[i] neq x then break
            update[i]->forward[i] := x->foreard[i]
        free(x)
        while list->level > 1 and list->header->forward[list->level] =NIL do 
            list->level := list->level -1

代码实现

代码实现时,上面的算法描述的前提是有一个最小key的header节点和最大key的tail节点,具体实现时还需注意K的最大最小值及比较操作。

#include<iostream>
#include<cstdlib>
#include<ctime>
#include<random>
#include<cmath>
#include<climits>
#include<vector>
#include<cassert>
#include<algorithm>

using namespace std;

const int MAX_LEVEL = 8;   // 用户定义的最大层数
const float P = 0.5;        // 用户设置的概率P

//跳跃列表节点类
template<class K, class V>
class SkipListNode {
public:
    SkipListNode() {}
    SkipListNode(K key, V value, int level);
    K key;
    V value;
    typedef SkipListNode<K, V>* NodePtr;
    NodePtr forward[MAX_LEVEL];
};

template<class K, class V>
SkipListNode<K, V>::SkipListNode(K key, V value, int level):key(key),value(value) {
    for (int i = 0; i < level; ++i) {
        this->forward[i] = nullptr;
    }
}

// 跳跃列表类
template<class K, class V>
class SkipList {
public:
typedef SkipListNode<K, V>* NodePtr;

    SkipList();
    SkipList(NodePtr header, NodePtr tail);
    ~SkipList();
    V* search(const K& key);
    void insert(const K& key,const V& value);
    void remove(const K& key);
    void print();
private:
    int randomLevel() const;
    NodePtr header;           // header节点
    NodePtr tail;             // tail节点
    int level;                // 当前层数
    int n;                    // 节点数量
};

template<class K, class V>
void SkipList<K, V>::print() {
    assert(this->header != nullptr);
    std::cout << "level=" << this->level << " count=" << this->n << endl;
    for (int i = this->level - 1; i >= 0 ; --i) {
        cout << "level" << i + 1 << ":";
        NodePtr pNode = this->header->forward[i];
        while (pNode != this->tail) {
            cout << "<" << pNode->key << ", " << pNode->value << ">  ";
            pNode = pNode->forward[i]; 
        }
        cout << endl;
    }
}

template<class K, class V>
SkipList<K, V>::SkipList() {
    this->header = nullptr;
    this->tail = nullptr;
    this->level = 0;
    this->n = 0;
}

template<class K, class V>
SkipList<K, V>::SkipList(NodePtr header, NodePtr tail) {
    this->header = header;
    this->tail = tail;
    this->level = 1;
    this->n = 0;
    for (int i = 0; i < MAX_LEVEL; ++i) {
        this->header->forward[i] = tail;
    }
}

template<class K, class V>
SkipList<K, V>::~SkipList() {
    NodePtr p = this->header;
    while (p->forward[0] != nullptr) {
        NodePtr tmp = p;
        p = p->forward[0];
        delete tmp;
    }
}

template<class K, class V>
int SkipList<K, V>::randomLevel() const {
    int lvl = 1;
    int maxLevel = (int)log(this->n) + 1;
    while (rand() % 100 < P * 100 && lvl < min(maxLevel, MAX_LEVEL)) {
        ++lvl;
    }

    return lvl;
}

template<class K, class V>
V* SkipList<K, V>::search(const K& key) {
    NodePtr x = this->header;
    for (int i = 0; i < this->level; ++i) {
        while (x->forward[i]->key < key) {
            x = x->forward[i];
        }
    }

    x = x->forward[0];      
    if (x->key == key) {
        return &x->value;
    } else {
        return nullptr;
    }
}

template<class K, class V>
void SkipList<K, V>::insert(const K& key,const V& value) {
    // 找到待调整的项
    NodePtr update[MAX_LEVEL];
    NodePtr ptr = this->header;
    for (int i = this->level - 1; i >= 0; --i) {
        while (ptr->forward[i]->key < key) {
            ptr = ptr->forward[i];
        }
        update[i] = ptr;
    }

    if (update[0]->forward[0]->key == key) {
        update[0]->forward[0]->value = value;
        return;
    }
    // 进行节点加入操作(与链表插入类似)
    const int lvl = this->randomLevel();        // 随机生成该节点层数
    if (lvl > this->level) {
        for (int i = this->level; i< lvl; ++i) {
            update[i] = this->header;
        }
        this->level = lvl;
    }

    NodePtr x = new SkipListNode<K, V>(key, value, lvl);
    for (int i = 0; i < this->level; ++i) {
        x->forward[i] = update[i]->forward[i];
        update[i]->forward[i] = x;
    }

    this->n++;
}

template<class K, class V>
void SkipList<K, V>::remove(const K& key) {
    NodePtr update[MAX_LEVEL];
    NodePtr x = this->header;
    for (int i = this->level -1; i >= 0; --i) {
        while (x->forward[i]->key < key) {
            x = x->forward[i];
        }
        update[i] = x;
    }

    x = x->forward[0];
    if (x->key == key) {
        for (int i = 0; i < this->level; ++i) {
            if (update[i]->forward[i] != x) 
                break;
            update[i]->forward[i] = x->forward[i];
        }
        delete x;
        this->n--;
        while (this->level > 1 && this->header->forward[this->level - 1] == this->tail) {
            this->level--;
        }
    }
} 

int main() {
    srand(time(nullptr));
    SkipListNode<int, int> *header = new SkipListNode<int, int>(INT_MIN, 0, 1);
    SkipListNode<int, int> *tail = new SkipListNode<int, int>(INT_MAX, 0, 1);
    SkipList<int, int>* pSkiplist = new SkipList<int, int>(header, tail);
    pSkiplist->insert(10, 10);
    pSkiplist->insert(15, 15);
    pSkiplist->insert(12, 12);
    pSkiplist->insert(18, 18);
    pSkiplist->insert(1, 1);
    pSkiplist->insert(121, 121);
    pSkiplist->insert(118, 118);
    pSkiplist->insert(10, 100);
    pSkiplist->print();
    int* v = pSkiplist->search(121);
    cout << "value is :" << *v << endl;
    pSkiplist->remove(118);
    pSkiplist->print();
    pSkiplist->insert(11860, 1180);
    pSkiplist->insert(11850, 1180);
    pSkiplist->insert(11840, 1180);
    pSkiplist->insert(11830, 1180);
    pSkiplist->insert(11820, 1180);

    pSkiplist->print();

    delete pSkiplist;
}
posted @ 2020-06-23 10:43  内核思考  阅读(581)  评论(0编辑  收藏  举报