每日算法总结——有序表系列——详解跳表(Java)

跳表的结构可以看成是多层链表,但只是链多,节点不多(可以想象成几根竹签(链)并排穿起几个鱼豆腐块长条(节点)😋)。每一个节点都是竖长条状的,也包含 keyvalue,但底层不是树结构,比较好实现。

跳表结构
  • 一个节点 <key, value> 会被几条链串起来,不是由 key 本身来决定的,而是随机的:

    • 随机函数只会随机 roll 出01,roll 的过程中,会求出当前节点的层数 level(即链的条数),level初始为0(最少只有1层,从0开始计数),当 roll 出0时,level++;roll 出1时,计算结束,此时level就是当前节点的层数。

    • 由随机函数可知,一个节点有 1 、 2 、 3 、 … 、 n 1、2、3、…、n 123n层的概率是 1 2 、 1 4 、 1 8 、 . . . 、 1 2 n \frac{1}{2}、\frac{1}{4}、\frac{1}{8}、...、\frac{1}{2^n} 214181...2n1,即处在上层的节点是逐渐减少的(随机方法有很多,上面的只是一种方法)。

  • 最底层是一条包含所有节点的单链表,上面的链表规模依次减少

  • 链表会有一个头结点,这个头结点是默认的,它比其他所有的节点都要小,所以排在最前面。而且该节点的高度也是当前链表 roll 出的最高层。

在这里插入图片描述

跳表操作
1、查找
  • 每次查找都是从最上面的链表开始找,依次往下走

  • 判断当前层所处位置的下一个节点与要找节点的大小关系:

    • 如果下一个节点大于要找节点,则去往下一层的链表继续查找
    • 如果下一个节点小于要找节点,则去往下一个节点继续查找
    • 如果等于,则直接返回
  • 比如现在要找 30 这个节点:

    在这里插入图片描述

    我们可以看到,借助上层链表,我们跳过了很多节点,起到加速的作用;整体的查询复杂度可以达到 O ( l o g   N ) O(log~N) O(log N)

2、插入
  • 首先我们会 roll 出要插入节点的层数,然后开始查找该节点插入的位置。

  • 整个查找过程其实与上面的查找操作类似,只不过需要从 roll 出的层开始查找,而且必须要深入到最后一层链表(为了修改底层的链表)

  • 以插入 14 为例,假设 roll 出了2
    在这里插入图片描述

3、删除
  • 首先判断跳表中是否存在要删除的元素,如果有则通过查找操作找到那个节点
  • 头结点的顶层开始,找到该层恰好小于 要删除元素的节点:
    • 如果该节点的当前层数小于等于删除节点的层数,则需要修改指针的指向,让该节点的指针指向删除节点的下一个节点,然后去往下一层
    • 否则,直接去往下一层
代码实现
public class SkipListMap {
    /**
     * 跳表的节点类
     */
    public static class SkipListNode {
        // 属性一:该节点代表的值
        public Integer value;
        // 属性二:该节点所指向的node列表
        public ArrayList<SkipListNode> nextNodes;
        public SkipListNode(Integer value) {
            this.value = value;
            nextNodes = new ArrayList<>();
        }
    }

    /**
     * 调表迭代器,本质就是遍历最后一层链表
     */
    public static class SkipListIterator implements Iterator<Integer> {
        SkipList list;
        SkipListNode current;

        public SkipListIterator(SkipList list) {
            this.list = list;
            this.current = list.getHead();
        }

        @Override
        public boolean hasNext() {
            return current.nextNodes.get(0) != null;
        }

        @Override
        public Integer next() {
            current = current.nextNodes.get(0);
            return current.value;
        }
    }

    /**
     * 跳表类
     */
    public static class SkipList {
        // 填表的头节点
        private final SkipListNode head;
        // 调表的最大高度
        private int maxLevel;
        // 当前容量
        private int size;
        private static final double PROBABILITY = 0.5;

        public SkipList() {
            size = 0;
            maxLevel = 0;
            head = new SkipListNode(null);
            head.nextNodes.add(null);
        }

        /**
         * 获取头节点
         */
        public SkipListNode getHead() {
            return head;
        }

        /**
         * 向跳表中添加元素
         */
        public void add(Integer newValue) {
            // 如果当前跳表里面不包含这个newValue
            if (!contains(newValue)) {
                // 向跳表里面添加节点,size增长
                size++;
                int level = 0;
                // 随机出层数
                while (Math.random() < PROBABILITY) {
                    level++;
                }
                // 如果随机出来的level层数大于最大层数maxLevel,则maxLevel增加
                while (level > maxLevel) {
                    head.nextNodes.add(null);
                    maxLevel++;
                }
                // 创建该newValue对应的跳表
                SkipListNode newNode = new SkipListNode(newValue);
                SkipListNode current = head;
                do {
                    // 针对每一层,找到第一个比newValue小的地方
                    current = findNext(newValue, current, level);
                    // 将当前跳表接单的第一个加上current的下一个节点,以这种方式将跳表全部连接起来
                    newNode.nextNodes.add(0, current.nextNodes.get(level));
                    // 将current的当前层节点放上newValue的节点
                    current.nextNodes.set(level, newNode);
                } while (level-- > 0);
            }
            // 如果跳表中存在此value,不进行任何操作
        }

        /**
         * 删除元素
         */
        public void delete(Integer deleteValue) {
            if (contains(deleteValue)) {
                SkipListNode deleteNode = find(deleteValue);
                size--;
                int level = maxLevel;
                SkipListNode current = head;
                do {
                    current = findNext(deleteNode.value, current, level);
                    if (deleteNode.nextNodes.size() > level) {
                        // 删除操作直接取消连接就可以了
                        current.nextNodes.set(level, deleteNode.nextNodes.get(level));
                    }
                } while (level-- > 0);
            }
        }

        /**
         * 返回跳表中,小于等于 e 的最大值
         */
        private SkipListNode find(Integer e) {
            // 给定元素e,查询其所在的跳表节点
            return find(e, head, maxLevel);
        }

        /**
         * 从节点current的level层开始,查找小于等于 e 的最大值并返回
         */
        private SkipListNode find(Integer e, SkipListNode current, int level) {
            do {
                current = findNext(e, current, level);
            } while (level-- > 0);
            // 一直查到最下面一层
            return current;
        }

        /**
         * 返回在当前 level 层,小于 e 的最大值
         */
        private SkipListNode findNext(Integer e, SkipListNode current, int level) {
            // 首先获取当前层的下一个节点
            SkipListNode next = current.nextNodes.get(level);
            while (next != null) {
                Integer value = next.value;
                // e < value  如果当前e小于下一层的节点值,则直接break,进入下一层
                if (lessThan(e, value)) {
                    break;
                }
                current = next;
                next = current.nextNodes.get(level);
            }
            // 如果下一层为null,直接返回给父函数find(),进入下一层
            return current;
        }

        public int size() {
            return size;
        }

        // 检查跳表中是否存在此value
        public boolean contains(Integer value) {
            // 首先根据指定的值查询value在跳表种存在的节点
            SkipListNode node = find(value);
            // 查到的节点值和当前的值一样,则表示已经查到了
            return node != null && node.value != null && equalTo(node.value, value);
        }

        public Iterator<Integer> iterator() {
            return new SkipListIterator(this);
        }

        private boolean lessThan(Integer a, Integer b) {
            return a.compareTo(b) < 0;
        }

        private boolean equalTo(Integer a, Integer b) {
            return a.compareTo(b) == 0;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值