跳表(go实现)

跳表的应用场景、优点

跳表(Skip List)是一种概率数据结构,它提供了一种平衡的折中方案,既保持了链表插入和删除操作的高效性,又通过多层索引实现了快速查找。以下是跳表的一些应用场景以及它的主要优点:

图解跳表文章链接

应用场景

  1. 数据库系统

    • 在某些数据库系统中,跳表可以用于实现高效的索引机制,尤其是在需要支持快速查找、插入和删除操作的情况下。
  2. 分布式系统

    • 例如在 Apache Cassandra 中,跳表被用来实现列族存储,以支持高效的范围查询。
  3. 内存中的数据结构

    • 当需要在内存中维护一个有序的数据集合,并且需要频繁地进行插入、删除和查找操作时,跳表是一个很好的选择。
  4. 缓存系统

    • 在一些缓存实现中,如 Redis 的 Sorted Set,使用了类似于跳表的数据结构来支持有序元素的高效访问。
  5. 实时分析

    • 对于需要实时处理大量数据并支持快速查询的应用,比如日志分析或在线广告系统,跳表可以提供良好的性能。

优点

  1. 平均时间复杂度低

    • 跳表的查找、插入和删除操作的时间复杂度都是 O(log n),其中 n 是节点数量。这使得它在处理大规模数据集时非常高效。
  2. 简单易实现

    • 相比于红黑树等平衡二叉搜索树,跳表的实现相对简单,不需要复杂的旋转操作来维持平衡。
  3. 动态调整

    • 跳表可以在运行时动态调整其高度,以适应数据的变化。这种灵活性使得它在处理数据增长或减少时更加适应。
  4. 并发友好

    • 由于跳表的操作通常只影响局部区域,因此更容易实现线程安全。在多线程环境中,跳表可以通过细粒度的锁来提高并发性能。
  5. 范围查询

    • 跳表支持高效的范围查询,这对于需要按顺序访问数据的应用非常有用。
  6. 内存效率

    • 跳表的空间开销相对较小,因为它不需要像 B 树那样为了平衡而保留额外的指针。

总之,跳表结合了链表和平衡树的优点,适用于需要同时支持高效查找、插入和删除操作的场景,特别是在对并发性和简单实现有要求的情况下。

代码

package main

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

// Node 表示跳表中的一个节点。
type Node struct {
	value int
	next  []*Node // next 是一个指针数组,指向不同层次的下一个节点。
}

// SkipList 表示跳表数据结构。
type SkipList struct {
	head  *Node   // 头结点,不存储任何值,但指向第一个真实节点。
	level int     // 当前跳表的层数。
}

// NewSkipList 创建一个新的跳表实例,并初始化头结点。
func NewSkipList(maxLevel int) *SkipList {
	head := &Node{next: make([]*Node, maxLevel)}
	return &SkipList{head: head, level: 1}
}

// Find 查找给定值是否存在。
func (sl *SkipList) Find(value int) bool {
	current := sl.head
	for i := sl.level - 1; i >= 0; i-- { // 从最上层开始
		// 在当前层向前移动,直到找到大于或等于目标值的节点。
		for current.next[i] != nil && current.next[i].value < value {
			current = current.next[i]
		}
		// 如果找到了目标值,返回 true。
		if current.next[i] != nil && current.next[i].value == value {
			return true
		}
	}
	return false
}

// Insert 向跳表中添加新值。
func (sl *SkipList) Insert(value int) {
	update := make([]*Node, sl.level)
	current := sl.head

	for i := sl.level - 1; i >= 0; i-- {
		for current.next[i] != nil && current.next[i].value < value {
			current = current.next[i]
		}
		update[i] = current
	}

	// 生成随机层数(这里简化为固定层数)
	level := 1
	newNode := &Node{value: value, next: make([]*Node, level)}

	for i := 0; i < level; i++ {
		newNode.next[i] = update[i].next[i]
		update[i].next[i] = newNode
	}

	// 更新跳表的层数
	if level > sl.level {
		for i := sl.level; i < level; i++ {
			sl.head.next[i] = newNode
		}
		sl.level = level
	}
}

// Delete 从跳表中删除指定值。
func (sl *SkipList) Delete(value int) bool {
	update := make([]*Node, sl.level)
	current := sl.head

	for i := sl.level - 1; i >= 0; i-- {
		for current.next[i] != nil && current.next[i].value < value {
			current = current.next[i]
		}
		update[i] = current
	}

	// 检查是否找到要删除的节点
	if current.next[0] != nil && current.next[0].value == value {
		// 删除节点
		for i := 0; i < sl.level; i++ {
			if update[i].next[i] != nil && update[i].next[i].value == value {
				update[i].next[i] = update[i].next[i].next[i]
			}
		}
		// 更新跳表的层数
		for sl.level > 1 && sl.head.next[sl.level-1] == nil {
			sl.level--
		}
		return true
	}
	return false
}

func main() {
	rand.Seed(time.Now().UnixNano())
	skipList := NewSkipList(3) // 创建一个最大层数为 3 的跳表。

	// 插入一些值到跳表中
	values := []int{5, 2, 9, 4, 8, 7, 1, 3, 6}
	for _, v := range values {
		skipList.Insert(v)
	}

	// 检查某些值是否存在
	fmt.Println("查找 5:", skipList.Find(5)) // 应该打印 true
	fmt.Println("查找 10:", skipList.Find(10)) // 应该打印 false

	// 删除值
	fmt.Println("删除 5:", skipList.Delete(5)) // 应该打印 true
	fmt.Println("再次查找 5:", skipList.Find(5)) // 应该打印 false
}

执行结果

查找 5: true
查找 10: false
删除 5: true
再次查找 5: false
posted @ 2024-11-28 16:30  Jikefan  阅读(144)  评论(0)    收藏  举报