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

相关推荐
科技小花2 小时前
数据治理平台架构演进观察:AI原生设计如何重构企业数据管理范式
数据库·重构·架构·数据治理·ai-native·ai原生
一江寒逸2 小时前
零基础从入门到精通MySQL(中篇):进阶篇——吃透多表查询、事务核心与高级特性,搞定复杂业务SQL
数据库·sql·mysql
D4c-lovetrain2 小时前
linux个人心得22 (mysql)
数据库·mysql
阿里小阿希2 小时前
CentOS7 PostgreSQL 9.2 升级到 15 完整教程
数据库·postgresql
荒川之神2 小时前
Oracle 数据仓库雪花模型设计(完整实战方案)
数据库·数据仓库·oracle
做个文艺程序员3 小时前
MySQL安全加固十大硬核操作
数据库·mysql·安全
不吃香菜学java3 小时前
Redis简单应用
数据库·spring boot·tomcat·maven
一个天蝎座 白勺 程序猿3 小时前
Apache IoTDB(15):IoTDB查询写回(INTO子句)深度解析——从语法到实战的ETL全链路指南
数据库·apache·etl·iotdb
不知名的老吴3 小时前
Redis的延迟瓶颈:TCP栈开销无法避免
数据库·redis·缓存
YOU OU3 小时前
三大范式和E-R图
数据库