算法 - SkipList实现

什么是跳表

从数据结构上来说,跳表是一个具有层次结构且有着顺序的链表,通过增加一个高度指针和维护顺序来换取高效的CRUD复杂度为logn,着相当于用空间换取时间结构如下

数据结构

@AllArgsConstructor
private static class Node<T extends Comparable<? super T>> {
    private Node right;
    private Node down;

    private T data;
}

Step 1 : 从最简单的操作入手:判断一个某个数据是否在跳表中

传统的链表需要从头到尾为遍历来判断,而在跳表中依旧如此只是增加了层次间迭代的维度

public <T extends Comparable<? super T>> boolean beExists(T target) {
    for (Node scanner = head; Objects.nonNull(scanner); scanner = scanner.down) {
        while (Objects.nonNull(scanner.right) && scanner.right.data.compareTo(target) < 0) {
            scanner = scanner.right;
        }
        if (Objects.nonNull(scanner.right) && scanner.right.data.equals(target)) {
            return true;
        }
    }
    return false;
}

这种遍历方式也叫right-down迭代

Step 2 : 删除一个元素

也是和传统List类似通过引用覆盖的方式删除

public <T extends Comparable<? super T>> boolean remove(T target) {
      Objects.requireNonNull(target, "target cannot be null");

      boolean beRemoveSuccess = false;

      for (Node scanner = head; Objects.nonNull(scanner); scanner = scanner.down) {
          //skip it when low target
          while (Objects.nonNull(scanner.right) && scanner.right.data.compareTo(target) < 0) {
              scanner = scanner.right;
          }

          //delete it when only equals
          if (Objects.nonNull(scanner.right) && scanner.right.data.compareTo(target) == 0) {
              scanner.right = scanner.right.right;
              beRemoveSuccess = true;
          }
      }

      return beRemoveSuccess;
  }

Step 3 : 新增元素

新增的差异很大,主要需要维护合理的高度

❓ 新增节点时要考虑Skip List的高度怎么维护 ?难点在于高度太高会影响维护的效率,太少有可能被拉成一个普通的排序链表。 在参考Redis源码时,主要使用扔骰子的方式来决定是否继续增加一层高度。

private final Random random = new Random();
private Node[] closestNodes = new Node[32];
private Node head = new Node(null, null, null);

public <T extends Comparable<? super T>> void add(T target) {
    int level = -1;
    
    //right-down search
    for (Node scanner = head; Objects.nonNull(scanner) ; scanner = scanner.down) {
        while (Objects.nonNull(scanner.right) && scanner.right.data.compareTo(target) < 0) {
            scanner = scanner.right;
        }

        //find equals or greater
        closestNodes[++level] = scanner;
    }

    boolean beNeedCreateNextNode = true;
    Node createdNode = null, closestNode;

    while (beNeedCreateNextNode && level > -1) {
        closestNode = closestNodes[level--];
        //create node, the key is update down pointer
        closestNode.right = (createdNode = new Node(closestNode.right, createdNode, target));
        //random decision
        beNeedCreateNextNode = random.nextBoolean();
    }
    
    if (beNeedCreateNextNode) {
        head = new Node(new Node(null, createdNode, target), head, null);
    }
}
posted @ 2021-03-29 23:25  密云不雨  阅读(64)  评论(0)    收藏  举报