Skip List
时间复杂度基于产生的索引层数的概率
跳表是对有序链表的改进
跳表的定义
interface SkipList<K : Comparable<K>, V> : KVOperation<K, V>, DataStructurePrintable {
/**
* 跳表的层数
*/
fun level(): Int
}
/**
* 默认最高层数
*/
const val DEFAULT_MAX_LEVEL = 16
/**
* 默认产生层数的概率
*/
const val DEFAULT_P = 0.5
跳表的实现
class SkipListImpl<K : Comparable<K>, V>(
val maxLevel: Int = DEFAULT_MAX_LEVEL,
val p: Double = DEFAULT_P
) : SkipList<K, V> {
companion object {
private const val MIN_LEVEL = 1
}
/**
* 随机
*/
private val random = Random(System.currentTimeMillis())
/**
* 哨兵节点
*/
private val head = SkipListNode<K, V>(null, null, this.maxLevel, this.p, true)
/**
* 跳表的层级,取决于跳表节点创建的随机出的层级
*/
private var skipListLevel = MIN_LEVEL
/**
* 跳表的节点个数
*/
private var nodeCount = 0
/**
* 跳表节点个数
*/
override fun size(): Int = nodeCount
/**
* 当前跳表存储的层级
*/
override fun level(): Int = this.skipListLevel
override fun contains(key: K): Boolean {
return get(key) != null
}
/**
* 查找的逻辑 跳表查找的核心就是 从上到下、每层尽量向右,最后在底层确认
*
* 高层 → 定位范围 低层 → 精确查找(最终确认节点)
*
* 优先向右查找,如果不满足,再向下查找
*/
override fun get(key: K): V? {
if (isEmpty()) return null
// 从哨兵节点 head 开始查找,所有的值都在 node.forward[level] 里面
// 两个维度,一个处理同行(向右),一个处理同列(向下)
var node = head
var levelIdx = level() - 1
while (levelIdx >= 0) {
val storageNode = node.getRight(levelIdx)
if (storageNode == null) {
// 存储值的节点不存在,直接向下查找
levelIdx--
} else { // 存储值的节点存在,作比较
val currKey = storageNode.key!!
when {
currKey == key -> return storageNode.value
key > currKey -> {
// 继续向右查找
node = storageNode
}
else -> { // key < currKey 向下查找
levelIdx--
}
}
}
}
return null
}
override fun insert(key: K, value: V) {
// 每一层 默认 head 为前驱节点
// 前驱的核心是每一层最后一个小于key的,每一层本身就是顺序列表,所以不需要从Head再遍历
val update = Array(maxLevel) { head }
var currNode = head
var levelIdx = level() - 1 // 从存储的最高的level开始,避免遍历无效的层数
while (levelIdx >= 0) {
val storageNode = currNode.getRight(levelIdx)
if (storageNode == null) {
update[levelIdx] = currNode
levelIdx--
} else {
val storageKey = storageNode.key!!
when {
key == storageKey -> {
// 更新的时候发现匹配到了值,直接更新,并退出
storageNode.value = value
return
}
key > storageKey -> {
// 继续向右查找,刷新当前的node
currNode = storageNode
}
else -> { // key < storageKey
update[levelIdx] = currNode // 这一层找到了
levelIdx-- // 继续向下
}
}
}
}
// 真实的插入操作
// 1. update中需要指向新的节点
// 2. 插入的节点需要更新索引
// 3. 那么从思路上来说,需要从下往上更新
val newNode = SkipListNode(key, value, this.maxLevel, this.p)
val newNodeLevel = newNode.getNodeLevel().apply {
if (this > skipListLevel) { // 更新levelCount
skipListLevel = this
}
}
// kotlin 1.9 新语法 ..< 左闭右开
// for (levelIndex in 0..<newNodeLevel) {
for (levelIndex in 0 until newNodeLevel) {
val updateNode = update[levelIndex] // 由于哨兵节点的设计,前驱节点一定存在
val updateRight = updateNode.getRight(levelIndex)
newNode.setRight(levelIndex, updateRight)
updateNode.setRight(levelIndex, newNode)
}
nodeCount++
}
override fun remove(key: K): V? {
// 删除节点的前驱节点
val update = arrayOfNulls<SkipListNode<K, V>>(level())
var node = head
var levelIdx = level() - 1 // 删除的时候对于节点的更新不需要从最高层
var target: SkipListNode<K, V>? = null
while (levelIdx >= 0) {
val storage = node.getRight(levelIdx)
if (storage == null) {
levelIdx--
} else {
val storageKey = storage.key!!
when {
storageKey == key -> { // 找到需要删除的节点
target = storage
update[levelIdx] = node
levelIdx-- // 继续寻找下一层
}
storageKey < key -> { // 目前的值小于需要删除的值
node = storage // 继续向右查找
}
else -> { // storageKey > key
levelIdx-- // 继续向下查找
}
}
}
}
if (target == null) {
println("not find target")
return null
}
// 更新索引
// 1. 对于被删除的节点,更新前驱节点的指向
// 2. 如果前驱节点没有指向过当前节点,则不管
// 3. 如果前驱节点指向了当前节点,更新到指向的节点的新节点
val targetLevel = target.getNodeLevel()
// 从最底层开始更新
for (levelIndex in 0 until targetLevel) {
// 如果存在前驱节点则更新
update[levelIndex]?.apply {
val left = this
left.setRight(levelIndex, target.getRight(levelIndex))
}
}
// 节点被删除了,重新更新跳表本身的层高,不能确定哪个是MAX,从上到下遍历一遍
// 4) 收缩最高层级:当顶层为空时向下收缩
while (skipListLevel > MIN_LEVEL && head.getRight(skipListLevel - 1) == null) {
skipListLevel--
}
nodeCount--
return target.value
}
override fun clear() {
this.skipListLevel = MIN_LEVEL
this.nodeCount = 0
for (i in 0 until head.getNodeLevel()) {
head.setRight(i, null)
}
}
private inner class SkipListNode<K : Comparable<K>, V>(
val key: K?, // 不可变,唯一标识
var value: V?, // 可变,更新值
val maxLevel: Int,
val p: Double,
head: Boolean = false
) {
// 节点的层数: 跳表的核心 概率化
private val nodeLevel: Int = if (head) maxLevel else randomLevel()
// 本质上由 forward[0] 构成了链表,其他层的都是索引
private val forward: Array<SkipListNode<K, V>?> = arrayOfNulls(maxLevel)
/**
* 获取指向的值
*/
fun getRight(level: Int): SkipListNode<K, V>? = forward[level]
/**
* 设置指向的值
*/
fun setRight(level: Int, node: SkipListNode<K, V>?) {
forward[level] = node
}
/**
* 获取节点的随机层数
*/
fun getNodeLevel(): Int = this.nodeLevel
/**
* 随机生成一个层数 [1, MAX_LEVEL],最终体现在创建的数组的大小上
*/
private fun randomLevel(): Int {
var level = 1
while (random.nextDouble() < p && level < this.maxLevel) {
level++
}
return level
}
}
override fun print() {
if (isEmpty()) {
println("SkipList(empty)")
return
}
// 底层(level 0)所有节点,作为列对齐基准;首列放入 HEAD
val columns = mutableListOf<SkipListNode<K, V>>()
columns.add(head)
var n = head.getRight(0)
while (n != null) {
columns.add(n)
n = n.getRight(0)
}
// 计算对齐宽度(考虑 HEAD 与所有 key 的宽度)
val maxKeyLen = columns.drop(1).maxOfOrNull { it.key.toString().length } ?: 0
val cellWidth = maxOf("(HEAD)".length, maxKeyLen) + 2
val arrowToken = "-> "
val arrowSpace = " ".repeat(arrowToken.length)
// 自顶向下逐层打印
for (lvl in level() - 1 downTo 0) {
// 1) 该层的 incoming 目标集合:凡是在该层能被某个前驱指到的节点,都算 incoming
val incoming = HashSet<SkipListNode<K, V>>()
var from = head.getRight(lvl)
while (from != null) {
incoming.add(from) // from 在该层有前驱(head 或上一节点)
from = from.getRight(lvl)
}
val sb = StringBuilder().apply { append("Level ").append(lvl).append(": ") }
// 2) 逐列打印:该列存在则打印 key;在“列间空隙”处,如果右侧列是 incoming,则画箭头
for (i in columns.indices) {
val node = columns[i]
val label = if (node === head) "(HEAD)"
else if (lvl < node.getNodeLevel()) node.key.toString()
else ""
sb.append(label.padEnd(cellWidth, ' '))
if (i < columns.lastIndex) {
val rightNode = columns[i + 1]
val drawArrow = incoming.contains(rightNode) // 右侧列若在本层为 incoming,则在这里画箭头
sb.append(if (drawArrow) arrowToken else arrowSpace)
}
}
println(sb.toString().trimEnd())
}
}
}
27 January 2026