跳表(Skip List)是一种数据结构,它通过在多个层次上添加额外的前向指针来提高有序数据的搜索效率。跳表与其他常见的有序数据结构(如二叉搜索树、平衡树如AVL树和红黑树、B树等)相比,具有其独特的优缺点:
跳表的优点
简单性: 跳表的算法和数据结构相对简单,容易理解和实现。与平衡树的复杂旋转和重新平衡相比,跳表的维护成本较低。
高效的搜索操作: 跳表可以提供接近二叉搜索树的搜索性能,平均时间复杂度为 O(logn)。
灵活的插入和删除: 跳表在插入和删除操作时,只需要调整相邻的指针,而不需要像在平衡树中那样复杂的重新平衡。
有序性: 与哈希表不同,跳表维护了元素的有序性,使得范围查询等操作更加高效。
跳表的缺点
空间消耗: 由于跳表在每个节点中存储多个指针,它通常会消耗比二叉树更多的内存空间。
随机性: 跳表的性能部分依赖于随机化过程,这可能导致性能的不稳定性。虽然在平均情况下表现良好,但最坏情况的表现可能不如某些确定性结构。
缓存不友好: 由于跳表的节点可能分布在内存的不同位置,它可能不如紧凑存储的数据结构(如数组)那样对缓存友好。
举个例子
跳表的 put 操作
想象一下你在一个有多层的购物中心里。每一层都有很多商店,而且每层都有楼梯和电梯连接到其他层。现在,你要在这个购物中心里开一家新店。
put 操作就像是在购物中心里找一个合适的位置来开设你的店铺。
选择楼层: 你首先决定你的店铺要开在哪些楼层。这就像跳表中随机决定节点应该在哪些层上有链接。
找到位置: 接着,你从最高层开始,寻找开设店铺的位置。你会经过每一家店,直到找到合适的空位。这就像在跳表中,从最高层开始向右搜索,直到找到合适的插入点。
开设店铺: 一旦找到位置,你就在那里开设你的店铺,并确保楼梯或电梯可以通到你的店铺。这在跳表中对应于将新节点插入到链表中,并更新相邻节点的指针。
跳表的 del 操作现在,想象你决定关闭你在购物中心的某个店铺。
del 操作就像是在购物中心中关闭并移除你的店铺。
寻找店铺: 你首先需要找到你的店铺。这就像在跳表中寻找一个特定的节点。
关闭并移除: 一旦找到,你就关闭店铺,并且移除所有通向这家店铺的楼梯和电梯的指示标志。这在跳表中对应于移除一个节点,并更新所有指向该节点的指针,确保它们指向下一个正确的节点。
调整楼层: 如果你关闭的是最高层的唯一店铺,那么整个楼层可能会被关闭。这就像在跳表中,如果删除节点后某一层没有任何节点了,我们就降低跳表的高度。
代码实现
go
package main
import (
"fmt"
"math/rand"
"time"
)
type node struct {
next []*node
key, val int
}
type SkipList struct {
head *node
}
func (s *SkipList) search(key int) *node {
move := s.head
for level := len(s.head.next) - 1; level >= 0; level-- {
for move.next[level] != nil && move.next[level].key < key {
move = move.next[level]
}
if move.next[level] != nil && move.next[level].key == key {
return move.next[level]
}
}
return nil
}
func (s *SkipList) Get(key int) (int, bool) {
if searchNode := s.search(key); searchNode != nil {
return searchNode.val, true
}
return -1, false
}
// Adjust the roll method to control the height distribution
func (s *SkipList) roll() int {
level := 0
for rand.Float32() < 0.5 { // Adjust the probability as needed
level++
}
return level
}
func (s *SkipList) Put(key, val int) {
if search := s.search(key); search != nil {
search.val = val
return
}
level := s.roll()
for len(s.head.next)-1 < level {
s.head.next = append(s.head.next, nil)
}
newNode := node{
next: make([]*node, level+1),
key: key,
val: val,
}
move := s.head
for l := level; l >= 0; l-- {
for move.next[l] != nil && move.next[l].key < key {
move = move.next[l]
}
newNode.next[l] = move.next[l]
move.next[l] = &newNode
}
}
func (s *SkipList) Del(key int) {
if _node := s.search(key); _node == nil {
return
}
move := s.head
for level := len(s.head.next) - 1; level >= 0; level-- {
for move.next[level] != nil && move.next[level].key < key {
move = move.next[level]
}
if move.next[level] != nil && move.next[level].key == key {
move.next[level] = move.next[level].next[level]
}
}
for len(s.head.next) > 1 && s.head.next[len(s.head.next)-1] == nil {
s.head.next = s.head.next[:len(s.head.next)-1]
}
}
func (s *SkipList) ceiling(target int) *node {
move := s.head
for level := len(s.head.next) - 1; level >= 0; level-- {
for move.next[level] != nil && move.next[level].key < target {
move = move.next[level]
}
if move.next[level] != nil && move.next[level].key == target {
return move.next[level]
}
}
return move.next[0]
}
func (s *SkipList) Range(start, end int) [][2]int {
ceilNode := s.ceiling(start)
if ceilNode == nil {
return [][2]int{}
}
var res [][2]int
for move := ceilNode; move != nil && move.key <= end; move = move.next[0] {
res = append(res, [2]int{move.key, move.val})
}
return res
}
func main() {
sl := createTestSkipList()
// Test Insertion
fmt.Println("Testing Insertion...")
sl.Put(3, 30)
sl.Put(1, 10)
sl.Put(2, 20)
fmt.Println("Insertion Passed")
// Test Search
fmt.Println("Testing Search...")
if val, found := sl.Get(2); !found || val != 20 {
fmt.Printf("Search Failed: expected 20, got %d\n", val)
} else {
fmt.Println("Search Passed")
}
// Test Deletion
fmt.Println("Testing Deletion...")
sl.Del(1)
if _, found := sl.Get(1); found {
fmt.Println("Deletion Failed: 1 should not exist")
} else {
fmt.Println("Deletion Passed")
}
// Test Ceiling Function
fmt.Println("Testing Ceiling Function...")
ceilNode := sl.ceiling(2)
if ceilNode == nil || ceilNode.key != 2 {
fmt.Printf("Ceiling Failed: expected key 2, got %d\n", ceilNode.key)
} else {
fmt.Println("Ceiling Passed")
}
// Test Range Query
fmt.Println("Testing Range Query...")
expected := [][2]int{{2, 20}, {3, 30}}
result := sl.Range(2, 3)
if len(result) != len(expected) {
fmt.Printf("Range Failed: expected length %d, got %d\n", len(expected), len(result))
} else {
allMatch := true
for i, pair := range expected {
if result[i] != pair {
fmt.Printf("Range Failed at index %d: expected %v, got %v\n", i, pair, result[i])
allMatch = false
break
}
}
if allMatch {
fmt.Println("Range Passed")
}
}
}
func createTestSkipList() *SkipList {
rand.Seed(time.Now().UnixNano())
sl := SkipList{head: &node{next: make([]*node, 1)}}
return &sl
}