Golang 程序实现跳跃表

go programmingserver side programmingprogramming

跳跃表是一种动态数据结构,可提供高效的插入、搜索和删除操作。在本文中,我们将演示该函数并探索在 Go 编程语言中实现跳跃表的算法概念。我们将为此演示编写两个不同示例。在第一个示例中,我们将使用随机化方法,在第二个示例中,我们将直接从随机塔结构构建跳跃表,以便更快地遍历。

解释

跳跃表作为一种数据结构,维护元素的排序列表,以便快速搜索,而无需平衡树的复杂性。这是通过创建具有不同跳跃距离的多个链接列表层来实现的,有助于更快地遍历到所需的元素。

语法

func newNode(vlue, lvl int) *node

该语法表示一种称为 `newNode()` 的方法,该方法定义为接受一个值和一个整数来指定塔的高度,并返回一个节点及其值集以及指向不同级别的后续节点的指针数组,用于 Skip List 实现。

func (sl *skipList) randomLvl() int

该语法表示一种称为 `randomLvl()` 的方法,该方法定义为使用概率参数和最大级别为 Skip List 中的新节点生成随机高度,以确定塔的高度高度。

算法

  • 首先用一个基数和一个最大层数初始化跳跃表。

  • 为具有随机层的元素创建节点,最终形成类似塔的结构。

  • 在层内水平连接节点,在层间垂直连接节点。

  • 从左上角开始搜索,然后向右或向下移动。

  • 对于插入,更新节点链接,同时考虑塔的高度。

示例 1

在此示例中,我们将使用随机化在 Go 中实现跳跃表。结构节点保存值和链接指针。函数 insert 通过遍历层添加元素,函数 search 定义为使用多级搜索方法查找元素。randomLvl() 方法生成塔的高度。我们利用每个节点中的下一个指针数组来创建塔结构,该结构直接存储每个节点塔的级别及其与下一个数组的连接。最后在主函数中,使用值初始化跳跃列表。最后,执行元素搜索。


package main
 
import (
    "fmt"
    "math/rand"
)
const (
    maxLvl = 16
	p    	= 0.5
)
type node struct {
	vlue int
	nxt  []*node
}
type skipList struct{ head *node }
func newNode(vlue, lvl int) *node {
    return &node{vlue: vlue, nxt: make([]*node, lvl)}
}
func (sl *skipList) insert(vlue int) {
    lvl := sl.randomLvl()
    newNode := newNode(vlue, lvl)
	crrent := sl.head
    for i := lvl - 1; i >= 0; i-- {
        for crrent.nxt[i] != nil && crrent.nxt[i].vlue < vlue { crrent = crrent.nxt[i] }
        newNode.nxt[i], crrent.nxt[i] = crrent.nxt[i], newNode
    }
}
func (sl *skipList) search(vlue int) bool {
    crrent := sl.head
    for i := len(crrent.nxt) - 1; i >= 0; i-- {
        	for crrent.nxt[i] != nil && crrent.nxt[i].vlue < vlue { crrent = crrent.nxt[i] }
    }
	return crrent.nxt[0] != nil && crrent.nxt[0].vlue == vlue
}
func (sl *skipList) randomLvl() int {
	lvl := 1
	for rand.Float64() < p && lvl < maxLvl { lvl++ }
	return lvl
}
func main() {
    sl := &skipList{head: newNode(0, maxLvl)}
    vlues := []int{3, 6, 9, 2, 5, 8, 1, 4, 7}
	for _, v := range vlues { sl.insert(v) }
	fmt.Print("Skip List: ")
	crrent := sl.head.nxt[0]
	for crrent != nil { fmt.Printf("%d -> ", crrent.vlue); crrent = crrent.nxt[0] }
	fmt.Println("nil")
	fmt.Println("Search 5:", sl.search(5))
	fmt.Println("Search 10:", sl.search(10))
}

输出

Skip List: 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9 -> nil
Search 5: true
Search 10: false

示例 2

在本示例中,我们将直接从随机塔结构中实现 Go 中的 Skip List,以便更快地进行遍历。通过创建类似塔的连接来插入元素,并通过多级比较进行类似的搜索。生成随机级别以保持平衡分布。每个级别由一个单独的链表表示,并通过链表节点管理连接。在主函数中,启动 Skip List,最后在其上执行元素搜索。


package main
import (
    "fmt"
	"math/rand"
)
const (
	maxLvl = 16
	p    	= 0.5
)
type node struct {
	vlue int
	nxt  []*node
}
type skipList struct{ head *node }
func newNode(vlue, lvl int) *node {
    return &node{vlue: vlue, nxt: make([]*node, lvl)}
}
func (sl *skipList) insert(vlue int) {
	lvl, crrent := sl.randomLvl(), sl.head
	for i := lvl - 1; i >= 0; i-- {
     	for crrent.nxt[i] != nil && crrent.nxt[i].vlue < vlue {
            crrent = crrent.nxt[i]
        }
    newNode := newNode(vlue, i+1)
    newNode.nxt[i], crrent.nxt[i] = crrent.nxt[i], newNode
    }
}
func (sl *skipList) search(vlue int) bool {
    crrent := sl.head
    for i := len(crrent.nxt) - 1; i >= 0; i-- {
    	for crrent.nxt[i] != nil && crrent.nxt[i].vlue < vlue {
           crrent = crrent.nxt[i]
        }
    }
    return crrent.nxt[0] != nil && crrent.nxt[0].vlue == vlue
}
func (sl *skipList) randomLvl() int {
    lvl := 1
    for rand.Float64() < p && lvl < maxLvl {
        lvl++
    }
    return lvl
}
func main() {
    sl := &skipList{head: newNode(0, maxLvl)}
    vlues := []int{3, 6, 9, 2, 5, 8, 1, 4, 7}
    for _, v := range vlues {
    	sl.insert(v)
    }
    fmt.Println("Skip List:")
    for crrent := sl.head.nxt[0]; crrent != nil; crrent = crrent.nxt[0] {
        fmt.Printf("%d -> ", crrent.vlue)
    }
    fmt.Println("nil")
    fmt.Println("Search 5:", sl.search(5))
    fmt.Println("Search 10:", sl.search(10))
}

输出

Skip List:
1 -> 2 -> 3 -> 5 -> 7 -> 8 -> 9 -> nil
Search 5: false
Search 10: false

实际实施

  • 游戏开发:在有排行榜或最高分的游戏场景中,跳过列表是保留玩家分数排序的绝佳方式。这使得更新和获取最高分更加容易。

  • 地理空间索引:跳过列表被发现是构建地理索引的一种潜在方式,有助于高效存储和检索地理数据。使用这些索引可以高效检索附近的地点或地区,这对于以位置为中心的应用程序(如地图服务)非常重要。

结论

跳过列表是一种多功能数据结构,兼具简单性和效率,非常适合通过快速搜索、插入和删除操作来维护有序数据。在本文中,我们通过两种不同的方法探讨了实现跳跃列表的概念:与链表实现相比,第一种方法更节省内存,因为它避免了每个节点单独指针的开销。与数组方法相比,下一种方法提供了更好的灵活性和动态调整大小的功能,但从更负面的角度来看,由于单个链表节点的开销,它会消耗更多内存。


相关文章