skiplist(跳表)算法實現

jope3014 8年前發布 | 1K 次閱讀 C/C++ 算法

跳表是平衡樹的一種替代的數據結構,但是和紅黑樹不相同的是,跳表對于樹的平衡的實現是基于一種隨機化的算法的,這樣也就是說跳表的插入和刪除的工作是比較簡單的。

下面來研究一下跳表的核心思想:

先從鏈表開始,如果是一個簡單的鏈表,那么我們知道在鏈表中查找一個元素I的話,需要將整個鏈表遍歷一次。

 

 如果是說鏈表是排序的,并且節點中還存儲了指向前面第二個節點的指針的話,那么在查找一個節點時,僅僅需要遍歷N/2個節點即可。

 

這基本上就是跳表的核心思想,其實也是一種通過“空間來換取時間”的一個算法,通過在每個節點中增加了向前的指針,從而提升查找的效率。

跳表的數據存儲模型 

 我們定義:

如果一個基點存在k個向前的指針的話,那么陳該節點是k層的節點。

一個跳表的層MaxLevel義為跳表中所有節點中最大的層數。

下面給出一個完整的跳表的圖示:

 

 那么我們該如何將該數據結構使用二進制存儲呢?通過上面的跳表的很容易設計這樣的數據結構:

定義每個節點類型:

//定義每個節點類型:
type nodeStructure struct {
    key     int // key值
    value   int // value值
    forward []*nodeStructure
}

上面的每個結構體對應著圖中的每個節點,如果一個節點是一層的節點的話(如7,12等節點),那么對應的forward將指向一個只含一個元素的數組,以此類推。

 定義跳表數據類型:

// 定義跳表數據類型
type listStructure struct {
    level  int            /* Maximum level of the list (1 more than the number of levels in the list) */
    header *nodeStructure /* pointer to header */
}

跳表數據類型中包含了維護跳表的必要信息,level表明跳表的層數,header如下所示:

 

初始化的過程很簡單,僅僅是生成下圖中紅線區域內的部分,也就是跳表的基礎結構:

//跳表初始化
func newList() *listStructure {
    var l *listStructure
    var i int
    // 申請list類型大小的內存
    l = &listStructure{}
    // 設置跳表的層level,初始的層為0層(數組從0開始)
    l.level = 0

    // 生成header部分
    l.header = newNodeOfLevel(MaxNumberOfLevels)
    // 將header的forward數組清空
    for i = 0; i < MaxNumberOfLevels; i++ {
        l.header.forward[i] = nil
    }
    return l
}

插入操作

由于跳表數據結構整體上是有序的,所以在插入時,需要首先查找到合適的位置,然后就是修改指針(和鏈表中操作類似),然后更新跳表的level變量。

 

func insert(l *listStructure, key int, value int) bool {
    var k int
    // 使用了update數組
    var update [MaxNumberOfLevels]*nodeStructure
    var p, q *nodeStructure
    p = l.header
    k = l.level
    fmt.Printf("list level: %v\n", k)

    for ; k >= 0; k-- {
        // 查找插入位置
        q = p.forward[k]
        for q != nil && q.key < key {
            p = q
            q = p.forward[k]
        }

        // 設置update數組
        update[k] = p

    } // 對于每一層進行遍歷 一直到最低層

    // 這里已經查找到了合適的位置,并且update數組已經
    // 填充好了元素 貌似不插入重復元素
    if q != nil && q.key == key {
        q.value = value
        return false
    }

    // 隨機生成一個層數
    k = randomLevel()
    fmt.Printf("random Level: %v\n", k)

    if k > l.level {
        // 如果新生成的層數比跳表的層數大的話
        // 增加整個跳表的層數
        l.level++
        k = l.level
        // 在update數組中將新添加的層指向l->header
        update[k] = l.header
    }

    // 生成層數個節點數目
    q = newNodeOfLevel(k + 1)
    q.key = key
    q.value = value

    // 每層插入節點
    for ; k >= 0; k-- {
        p = update[k]
        q.forward[k] = p.forward[k]
        p.forward[k] = q
    }

    // 如果程序運行到這里,程序已經插入了該節點
    return true
}

刪除某個節點

和插入是相同的,首先查找需要刪除的節點,如果找到了該節點的話,那么只需要更新指針域,如果跳表的level需要更新的話,進行更新。

 

func delete(l *listStructure, key int) bool {
    var k, m int
    // 生成一個輔助數組update
    var update [MaxNumberOfLevels]*nodeStructure
    var p, q *nodeStructure
    p = l.header
    k = l.level
    m = l.level

    //指向該節點對應層的前驅節點 一直到最低層
    for ; k >= 0; k-- {

        q = p.forward[k]
        for q != nil && q.key < key {
            p = q
            q = p.forward[k]
        }
        update[k] = p

    }

    // 如果找到了該節點,才進行刪除的動作
    if q != nil && q.key == key {
        // 指針運算
        for k = 0; k <= m && update[k].forward[k] == q; k++ {
            // 這里可能修改l.header.forward數組的值的
            p = update[k]
            p.forward[k] = q.forward[k]
        }

        // 如果刪除的是最大層的節點,那么需要重新維護跳表的
        // 層數level
        for l.header.forward[m] == nil && m > 0 {
            m--
        }

        l.level = m
        return true

    } else {
        return false

    }
}

skiplist完整代碼

package main

import (
    "fmt"
    "math/rand"
    "time"
)

//定義每個節點類型:
type nodeStructure struct {
    key     int // key值
    value   int // value值
    forward []*nodeStructure
}

// 定義跳表數據類型
type listStructure struct {
    level  int            /* Maximum level of the list (1 more than the number of levels in the list) */
    header *nodeStructure /* pointer to header */
}

const (
    MaxNumberOfLevels = 11
    MaxLevel          = 10
)

// newNodeOfLevel生成一個nodeStructure結構體,同時生成l個*nodeStructure數組指針
//#define newNodeOfLevel(l) (*nodeStructure)malloc(sizeof(struct nodeStructure)+(l)*sizeof(nodeStructure *))

func newNodeOfLevel(level int) *nodeStructure {
    nodearr := make([]*nodeStructure, level) //new([level]*node)
    return &nodeStructure{forward: nodearr}
}

//跳表初始化
func newList() *listStructure {
    var l *listStructure
    var i int
    // 申請list類型大小的內存
    l = &listStructure{}
    // 設置跳表的層level,初始的層為0層(數組從0開始)
    l.level = 0

    // 生成header部分
    l.header = newNodeOfLevel(MaxNumberOfLevels)
    // 將header的forward數組清空
    for i = 0; i < MaxNumberOfLevels; i++ {
        l.header.forward[i] = nil
    }
    return l
}

func randomLevel() int {
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    return r.Intn(MaxLevel)
}

func insert(l *listStructure, key int, value int) bool {
    var k int
    // 使用了update數組
    var update [MaxNumberOfLevels]*nodeStructure
    var p, q *nodeStructure
    p = l.header
    k = l.level

    for ; k >= 0; k-- {
        // 查找插入位置
        q = p.forward[k]
        for q != nil && q.key < key {
            p = q
            q = p.forward[k]
        }

        // 設置update數組
        update[k] = p

    } // 對于每一層進行遍歷 一直到最低層

    // 這里已經查找到了合適的位置,并且update數組已經
    // 填充好了元素 貌似不插入重復元素
    if q != nil && q.key == key {
        q.value = value
        return false
    }

    // 隨機生成一個層數
    k = randomLevel()

    if k > l.level {
        // 如果新生成的層數比跳表的層數大的話
        // 增加整個跳表的層數
        l.level++
        k = l.level
        // 在update數組中將新添加的層指向l->header
        update[k] = l.header
    }

    // 生成層數個節點數目
    q = newNodeOfLevel(k + 1)
    q.key = key
    q.value = value

    // 每層插入節點
    for ; k >= 0; k-- {
        p = update[k]
        q.forward[k] = p.forward[k]
        p.forward[k] = q
    }

    // 如果程序運行到這里,程序已經插入了該節點
    return true
}

func delete(l *listStructure, key int) bool {
    var k, m int
    // 生成一個輔助數組update
    var update [MaxNumberOfLevels]*nodeStructure
    var p, q *nodeStructure
    p = l.header
    k = l.level
    m = l.level

    //指向該節點對應層的前驅節點 一直到最低層
    for ; k >= 0; k-- {

        q = p.forward[k]
        for q != nil && q.key < key {
            p = q
            q = p.forward[k]
        }
        update[k] = p

    }

    // 如果找到了該節點,才進行刪除的動作
    if q != nil && q.key == key {
        // 指針運算
        for k = 0; k <= m && update[k].forward[k] == q; k++ {
            // 這里可能修改l.header.forward數組的值的
            p = update[k]
            p.forward[k] = q.forward[k]
        }

        // 如果刪除的是最大層的節點,那么需要重新維護跳表的
        // 層數level
        for l.header.forward[m] == nil && m > 0 {
            m--
        }

        l.level = m
        return true

    } else {
        return false

    }
}

func search(l *listStructure, key int) int {
    var k int

    var p, q *nodeStructure
    p = l.header
    k = l.level

    //指向該節點對應層的前驅節點 一直到最低層
    for ; k >= 0; k-- {

        q = p.forward[k]
        for q != nil && q.key < key {
            q = q.forward[k]
        }

        if q != nil && q.key == key {
            return q.value
        }
    }

    if q != nil && q.key == key {
        return q.value
    } else {
        return -1
    }
}

func main() {
    l := newList()

    insert(l, 3, 30)
    insert(l, 6, 60)
    insert(l, 7, 70)
    insert(l, 9, 90)

    delete(l, 9)

    insert(l, 12, 120)
    insert(l, 17, 170)
    insert(l, 19, 190)

    fmt.Printf("skiplist:%v\n", search(l, 12))
    fmt.Printf("skiplist:%v\n", search(l, 9))
}

程序輸出結果:

skiplist:120
skiplist:-1


 本文由用戶 jope3014 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!