golang高效跳表(Skiplist)数据结构实现插件库skiplist的使用
golang高效跳表(Skiplist)数据结构实现插件库skiplist的使用
skiplist是一个参考Redis zskiplist实现的Golang跳表数据结构库。跳表是一种概率平衡的数据结构,能够在O(log n)时间内完成查找、插入和删除操作。
使用方法
下面是一个完整的使用示例,展示了如何创建跳表、插入元素、遍历元素以及获取元素排名:
package main
import (
"fmt"
"github.com/gansidui/skiplist"
"log"
)
// 定义User结构体,作为跳表存储的元素
type User struct {
score float64
id string
}
// 实现Less方法,定义元素的比较规则
func (u *User) Less(other interface{}) bool {
if u.score > other.(*User).score {
return true
}
if u.score == other.(*User).score && len(u.id) > len(other.(*User).id) {
return true
}
return false
}
func main() {
// 创建User切片
us := make([]*User, 7)
us[0] = &User{6.6, "hi"}
us[1] = &User{4.4, "hello"}
us[2] = &User{2.2, "world"}
us[3] = &User{3.3, "go"}
us[4] = &User{1.1, "skip"}
us[5] = &User{2.2, "list"}
us[6] = &User{3.3, "lang"}
// 创建跳表并插入元素
sl := skiplist.New()
for i := 0; i < len(us); i++ {
sl.Insert(us[i])
}
// 遍历跳表并打印元素
for e := sl.Front(); e != nil; e = e.Next() {
fmt.Println(e.Value.(*User).id, "-->", e.Value.(*User).score)
}
fmt.Println()
// 获取元素排名
rank1 := sl.GetRank(&User{2.2, "list"})
rank2 := sl.GetRank(&User{6.6, "hi"})
if rank1 != 6 || rank2 != 1 {
log.Fatal()
}
// 根据排名获取元素
if e := sl.GetElementByRank(2); e.Value.(*User).score != 4.4 || e.Value.(*User).id != "hello" {
log.Fatal()
}
}
/* 输出:
hi --> 6.6
hello --> 4.4
lang --> 3.3
go --> 3.3
world --> 2.2
list --> 2.2
skip --> 1.1
*/
主要功能说明
- 插入元素:使用
Insert
方法将元素添加到跳表中 - 遍历元素:使用
Front
和Next
方法可以顺序遍历跳表中的所有元素 - 获取排名:
GetRank
方法可以获取元素在跳表中的排名 - 根据排名获取元素:
GetElementByRank
方法可以根据排名获取对应的元素
比较规则
需要为存储在跳表中的类型实现Less
方法,定义元素的比较规则。在上面的例子中,我们首先比较score
字段,如果score
相同则比较id
的长度。
许可证
该项目使用MIT许可证。
更多关于golang高效跳表(Skiplist)数据结构实现插件库skiplist的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html
1 回复
更多关于golang高效跳表(Skiplist)数据结构实现插件库skiplist的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
Golang 高效跳表(Skiplist)实现与使用
跳表(Skiplist)是一种概率性的有序数据结构,它通过多级索引实现平均O(log n)时间复杂度的查找、插入和删除操作,是平衡树的一种替代方案。
常用跳表库推荐
在Go生态中,有几个优秀的跳表实现库:
- github.com/ryszard/goskiplist - 一个稳定且功能完整的跳表实现
- github.com/sean-public/skiplist - 高性能并发安全跳表
- github.com/huandu/skiplist - 支持泛型的跳表实现
使用 huandu/skiplist 示例
下面以 huandu/skiplist 为例展示跳表的基本用法:
package main
import (
"fmt"
"github.com/huandu/skiplist"
)
func main() {
// 创建一个跳表实例,键为int类型,值为string类型
list := skiplist.New(skiplist.Int)
// 插入元素
list.Set(3, "数据3")
list.Set(1, "数据1")
list.Set(4, "数据4")
list.Set(2, "数据2")
// 获取元素
if value, ok := list.GetValue(2); ok {
fmt.Println("键2对应的值:", value) // 输出: 键2对应的值: 数据2
}
// 遍历所有元素
fmt.Println("按顺序遍历跳表:")
for elem := list.Front(); elem != nil; elem = elem.Next() {
fmt.Printf("键: %v, 值: %v\n", elem.Key(), elem.Value)
}
// 范围查询
fmt.Println("范围查询[2,4):")
elem := list.Find(2)
end := list.Find(4)
for elem != nil && elem != end {
fmt.Printf("键: %v, 值: %v\n", elem.Key(), elem.Value)
elem = elem.Next()
}
// 删除元素
list.Remove(3)
fmt.Println("删除键3后跳表长度:", list.Len())
// 清空跳表
list.Init()
fmt.Println("清空后跳表长度:", list.Len())
}
并发安全跳表实现
如果需要并发安全的跳表,可以使用 github.com/sean-public/skiplist:
package main
import (
"fmt"
"sync"
"github.com/sean-public/skiplist"
)
func main() {
list := skiplist.New()
var wg sync.WaitGroup
// 并发插入
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
list.Set(float64(i), fmt.Sprintf("value%d", i))
}(i)
}
wg.Wait()
// 并发读取
for i := 0; i < 10; i++ {
wg.Add(1)
go func(i int) {
defer wg.Done()
if val, ok := list.Get(float64(i)); ok {
fmt.Printf("键%v的值: %v\n", i, val)
}
}(i)
}
wg.Wait()
}
自定义比较器
huandu/skiplist 支持自定义比较器:
package main
import (
"fmt"
"github.com/huandu/skiplist"
)
type Person struct {
Name string
Age int
}
func main() {
// 创建自定义比较器
comparator := skiplist.LessThanFunc(func(lhs, rhs interface{}) bool {
p1 := lhs.(Person)
p2 := rhs.(Person)
if p1.Name != p2.Name {
return p1.Name < p2.Name
}
return p1.Age < p2.Age
})
list := skiplist.New(comparator)
list.Set(Person{"Alice", 30}, "Alice的数据")
list.Set(Person{"Bob", 25}, "Bob的数据")
list.Set(Person{"Alice", 25}, "另一个Alice的数据")
// 查找
if elem := list.Get(Person{"Alice", 25}); elem != nil {
fmt.Println("找到:", elem.Value) // 输出: 找到: 另一个Alice的数据
}
}
性能考虑
- 跳表的空间复杂度为O(n),比普通链表高
- 对于内存敏感的场景,可以调整跳表的概率因子(P值)
- 在Go中,跳表通常比平衡树实现更简单且性能相当
- 对于读多写少的场景,考虑使用并发安全实现
跳表特别适合需要有序数据且需要高效查找、插入和删除的场景,如排行榜、范围查询等应用。