1. 程式人生 > >探索Skip List (跳躍表)

探索Skip List (跳躍表)

mark 鏈表 基本 來看 http earch void 速度 解釋

附William Pugh的論文 《Skip Lists: A Probabilistic Alternative to Balanced Trees》

寫在前面

以下內容針對的是Skip List的插入和刪除,建議你先到其他地方大概了解一下Skip List長什麽樣子的,然後再過來看看這篇,最好還是看一眼論文先,部分挺容易看懂的。Redis中的Sorted Set基本就是使用Skip List,只是稍作修改。


初識 Skip List

Skip List 是一種數據結構,實質上為一個鏈表,專門用於存儲有序元素,提供的查找速度可與平衡二叉樹媲美,優點是實現簡單。

技術分享圖片

論文中Skip List就是長上面這樣的,每個節點有多個forward指針,指向在其後面的元素。將forward指針分層,稱為level

,level為1的那層就是單純的有序單鏈表,隨著層次遞增,元素會越來越少。比如level的取值範圍可以是[1, 32]


Skip List 的插入

先快速看一眼下面翻譯過來的偽碼實現。

void Insert(list, searchKey, newValue)
{
    local update[1..MaxLevel];
    x = list->header;
    // 查找searchKey應存放的位置
    for(i = list->level to 1)
    {
        while(x->forward[i]->key < searchKey)
            x = x->forward[i];
        // 位置關系: x->key < searchKey <= x->forward[i]->key 
        update[i] = x;  // 看上行註釋便知update保存的是什麽
    }
    x = x->forward[1]; // 這在最低層
    if(x->key == searchKey)
    {
        // 已有相同的key,替換即可
        x->value = newValue;
    }
    else 
    {
        lv = randomLevel();  // 為新節點隨機取個level
        if(lv > list->level) // 特殊處理:新節點level比當前最大level高
        {
            for(i = list->level+1 to lv)
                update[i] = list->header;
            list->level = lv;
        }
        x = createNode(v, searchKey, newValue);
        for(i = 1 to lv)    // 調整相關指針
        {
            x->forward[i] = update[i]->forward[i];
            update[i]->forward[i] = x;
        }
    }
}

實現原理是,用一個update數組保存"最大且小於searchKey的元素",用它來調整涉及到的指針指向。搜索時從高層往低層搜索,順便記錄update數組,調整指針時從低層往高層調整。可能出現的情況是,新節點的level大於原來list的最大level,此時需要更新一下list的最大level。

randomLevel()比較容易實現,就是拋硬幣法,返回一個數字n表示拋了n+1次才出現反面,但要求n<=MaxLevel。


Skip List 的刪除

void Delete(list, searchKey)
{
    int update[1..MaxLevel];
    x = list->header;
    // 查找searchKey的存放位置
    for(i = list->level to 1)
    {
        while(x->forward[i]->key < searchKey)
            x = x->forward[i];
        update[i] = x;
    }
    x = x->forward[i];
    if(x->key == searchKey) // 若命中,則刪
    {
        // 調整指向x的指針
        for(i = 1 to list->level)
        {
            if(update[i]->forward[i] != x) break;
            update[i]->forward[i] = x->forward[i]
        }
        free(x);
        // 可能需要更新list的max level
        while(list->level > 1 && !list->header->forward[list->level]) 
            list->level = list->level - 1;
    }
}

看過Insert之後,這個不用解釋也能看懂了。

探索Skip List (跳躍表)