skiplist(跳表)算法實現
跳表是平衡樹的一種替代的數據結構,但是和紅黑樹不相同的是,跳表對于樹的平衡的實現是基于一種隨機化的算法的,這樣也就是說跳表的插入和刪除的工作是比較簡單的。
下面來研究一下跳表的核心思想:
先從鏈表開始,如果是一個簡單的鏈表,那么我們知道在鏈表中查找一個元素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 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!