Redis(128)Redis的跳表(Skip List)是如何实现的?

在 Redis 中,跳表(Skip List)是一种用于实现有序集合(Sorted Set)底层数据结构的算法。与传统的平衡树(如红黑树)相比,跳表结构相对简单,并且在实现上更加直观。

跳表通过在有序链表上构建多级索引来加速搜索操作,跳表的每一层都是一个有序链表,且每一层的元素是下一层的子集,通过多级索引,可以在对数级别的时间复杂度内进行插入、删除和查找操作。

跳表的基本结构

一个跳表节点包含以下几个部分:

  • score:节点的分数,用于排序。
  • value:节点的值。
  • forward:一个数组,包含指向不同层级的下一个节点的指针。
  • level:节点的层级。

跳表的具体实现

下面是跳表的基本实现,包括节点的定义、跳表的初始化、插入、删除和查找操作。

节点定义

首先,定义跳表节点结构。

java 复制代码
class SkipListNode {
    double score;
    String value;
    SkipListNode[] forward;

    public SkipListNode(int level, double score, String value) {
        this.score = score;
        this.value = value;
        this.forward = new SkipListNode[level + 1];
    }
}

跳表定义

定义跳表结构。

java 复制代码
class SkipList {
    private static final int MAX_LEVEL = 16;
    private static final double P = 0.5;
    private SkipListNode head;
    private int level;

    public SkipList() {
        this.level = 0;
        this.head = new SkipListNode(MAX_LEVEL, 0, null);
    }

    // 随机生成节点层级
    private int randomLevel() {
        int lvl = 0;
        while (Math.random() < P && lvl < MAX_LEVEL) {
            lvl++;
        }
        return lvl;
    }

    // 插入节点
    public void insert(double score, String value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].score < score) {
                x = x.forward[i];
            }
            update[i] = x;
        }
        int lvl = randomLevel();
        if (lvl > level) {
            for (int i = level + 1; i <= lvl; i++) {
                update[i] = head;
            }
            level = lvl;
        }
        x = new SkipListNode(lvl, score, value);
        for (int i = 0; i <= lvl; i++) {
            x.forward[i] = update[i].forward[i];
            update[i].forward[i] = x;
        }
    }

    // 删除节点
    public void delete(double score, String value) {
        SkipListNode[] update = new SkipListNode[MAX_LEVEL + 1];
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].score < score) {
                x = x.forward[i];
            }
            update[i] = x;
        }
        x = x.forward[0];
        if (x != null && x.score == score && x.value.equals(value)) {
            for (int i = 0; i <= level; i++) {
                if (update[i].forward[i] != x) break;
                update[i].forward[i] = x.forward[i];
            }
            while (level > 0 && head.forward[level] == null) {
                level--;
            }
        }
    }

    // 查找节点
    public SkipListNode search(double score) {
        SkipListNode x = head;
        for (int i = level; i >= 0; i--) {
            while (x.forward[i] != null && x.forward[i].score < score) {
                x = x.forward[i];
            }
        }
        x = x.forward[0];
        if (x != null && x.score == score) {
            return x;
        }
        return null;
    }
}

示例使用

下面是一个简单的示例,展示如何使用上述跳表实现插入、删除和查找操作。

java 复制代码
public class SkipListExample {
    public static void main(String[] args) {
        SkipList skipList = new SkipList();

        // 插入节点
        skipList.insert(3.0, "Alice");
        skipList.insert(1.0, "Bob");
        skipList.insert(2.0, "Charlie");

        // 查找节点
        SkipListNode node = skipList.search(2.0);
        if (node != null) {
            System.out.println("Found node: " + node.value);
        } else {
            System.out.println("Node not found.");
        }

        // 删除节点
        skipList.delete(1.0, "Bob");

        // 查找被删除的节点
        node = skipList.search(1.0);
        if (node != null) {
            System.out.println("Found node: " + node.value);
        } else {
            System.out.println("Node not found.");
        }
    }
}

Redis 中使用跳表的优势

  1. 时间复杂度:跳表的插入、删除和查找操作的平均时间复杂度都是 (O(\log n)),与平衡树相当,但实现更为简单。
  2. 空间复杂度:跳表在最坏情况下的空间复杂度为 (O(n \log n)),但在实际使用中,空间效率较高。
  3. 实现简单:相对于红黑树和 AVL 树等平衡树,跳表的实现较为简单,易于理解和调试。

Redis 选择跳表作为有序集合的数据结构,主要是因为跳表在实现排序、范围查询等操作时,性能优越且实现简单。希望这段代码和解释能帮助你更好地理解跳表的原理和实现。

相关推荐
小小张说故事5 小时前
Python 装饰器把函数"搞丢"了?5 个必踩的坑与完整避坑对照表
后端·python
大圣编蚕6 小时前
Spring AOP 失效排查:从原理到实战的完整指南
java·后端·spring
AINative软件工程6 小时前
LLM 多租户 Prompt Isolation 工程实践:为 100 个租户定制 Prompt,但别让它们互相污染
后端·llm
EatFan6 小时前
MCP 从概念到落地:Java(Spring AI Alibaba)与 .NET 双栈接入实操对比
java·人工智能·后端·spring·.net·java后端·mcp
专业程序开发源6 小时前
springboot全民健身和饮食健康管理系统29158-计算机课程设计、毕业设计
java·spring boot·后端·python·django·php·课程设计
梦帮科技7 小时前
【3.0修订版】 RNS 代币架构:ERC20 五件套扩展与六钱包分配
数据结构·后端·算法·架构·node.js·区块链·php
IT_陈寒7 小时前
SpringBoot自动配置的坑:你以为的捷径可能是弯路
前端·人工智能·后端
vx_Biye_Design7 小时前
springboot小区管理系统68491-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·课程设计·express
专业程序开发源8 小时前
springboot外卖系统94294-计算机课程设计、毕业设计
java·vue.js·spring boot·后端·spring·php·课程设计
FYKJ_20108 小时前
springboot心理健康管理系统86254-计算机课程设计、毕业设计
vue.js·spring boot·后端·python·mysql·django·课程设计