JDK源码阅读计划(Day13) ConcurrentSkipListMap & 跳表学习

本文介绍了跳表的概念,通过对比两种跳表结点设计,探讨了空间效率,并以第二种方式手写了跳表实现。接着详细讲解了ConcurrentSkipListMap的查找、插入和删除操作,指出其线程安全特性源于CAS操作和VarHandle的使用,最后提到了Redis的跳表实现作为对比。
摘要由CSDN通过智能技术生成

跳表

在这里插入图片描述
上图可以看到,一个有序单链表,查找某元素的平均时间复杂度为O(n)

跳表本质上是在有序链表上建立多层索引,以实现二分查找。以空间换时间的思想,实现增删查改平均时间复杂度为O(lgn)

而skipList的结构可能有2种:

第一种是每个结点会指向向右和向下的结点,像ConcurrentSkipListMap就是这么设计的

  /**
     * Index nodes represent the levels of the skip list.
     */
    // 跳表索引,存储右侧跟下侧的索引,组成一个十字链表
    static final class Index<K,V> {
   
        final Node<K,V> node;  // currently, never detached
        
        final Index<K,V> down;
        Index<K,V> right;
        
        Index(Node<K,V> node, Index<K,V> down, Index<K,V> right) {
   
            this.node = node;
            this.down = down;
            this.right = right;
        }
    }

下图就是一个好例子:
在这里插入图片描述
我在面试阿里云的时候笔试题就是手写跳表,当时我也打算是这么设计结点的结构。但面试官提到,这么做的话其实会非常浪费空间。首先最底层的原始链表都是没有向下结点,最后一个结点也没有向右结点,虽然next和right都是引用,但是未免还是有点浪费…更重要的是,假设跳表有10层,那么假设要插入一个新的节点,可能要在10层(不一定10层,这个是随机的)中都添加这个节点,虽然每一层next和right可能不同,但还是会造成数据冗余对吧。

因此另外一种定义结点的方式是这样的:

class Node{
   
      int data;
      Node[] forward; //forward[i]表示处于第i层的结点Node的下一个结点
                      //随机高度数 k决定节点的高度 h,节点的高度 h决定节点中forword的长度;}

每一个结点有一个结点数组,可以指向其每一层指向的结点。假设这个Node分布在10层中,那么forward长度就是10,forward[0]是其在第一层的后继节点…以此类推

那么每次插入一个新节点只需要new 一个Node,forward本质上也是一个数组的引用。那么总比上面那种可能要new 10次要强吧?

那我们以第二种定义方式继续讨论:

在这里插入图片描述

假设要找元素15

1.从head结点开始,从最顶层的1开始找,下一个结点为8,跳到8
2.下一个节点为18,从8的下一层开始寻找
3.在最底层依次经过8,10,13最后找到15

可以把除了最底层链表的结点都看作是索引层,通过额外引入索引节点,达到查询时候忽略某些节点的效果,本质上是以空间换时间

这里参考跳表实现尝试以这种定义方式手写一个跳表轮子加深理解,源代码在这skipList

  • find

与第一种定义的方法不同的是,这种方法必须要在原始链表中查找元素

public Node find(int val) {
   

        //begin with head node
        Node p = head;
        for (int l = levelCount - 1; l >= 0; l--) {
   
            while (p.forward[l] != null && p.forward[l].data < val) {
   
                p = p.forward[l];
            }
        }
        if(p.forward[0]!=null
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值