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

相关推荐
庞轩px几秒前
第四篇:多级缓存架构——Caffeine + Redis + MySQL 三级协同
java·redis·mysql·读写分离·caffeine·本地缓存
x***r15114 分钟前
Redis-x64-3.2.100安装步骤详解(附Redis服务注册与配置)
redis
努力努力再努力wz19 分钟前
【MySQL进阶系列】一文打通事务机制:从锁、Undo Log 到 MVCC 与隔离级别
c语言·数据结构·数据库·c++·mysql·算法·github
PaperData37 分钟前
2000-2025年《中国县域统计年鉴》pdf+excel版(附赠面板数据)
数据库·人工智能·数据分析·pdf·经管
冷小鱼43 分钟前
从关系模型(SQL)基石到AI与信创时代的智能查询语言
数据库·sql
EXnf1SbYK1 小时前
Redis分布式锁进阶第十二篇:全系列终极兜底复盘 + 锁架构巡检落地 + 线上零事故收尾方案
redis·分布式·架构
LLON erva1 小时前
Redis-配置文件
数据库·redis·oracle
童话ing1 小时前
【Redis】026 互联网大厂 Redis 面试高频题
数据库·redis·面试
钰衡大师1 小时前
Activiti 7 工作流技术文档
java·数据库·spring boot
Treh UNFO1 小时前
nginx的重定向
大数据·数据库·nginx