算法导论笔记 六 跳跃表

Posted by Yinode on Saturday, July 21, 2018

TOC

跳跃表(skiplists)

跳跃表是一种增强版的链表,他能在O(lgn)的时间内完成搜索。

首先,我们熟知的链表本身具有的特性就是容易删除与增加,但是在搜索的时候会使用O(n)的时间。主要的原因就是链表本身访问起来必须的连续的,也许我们可能在某个节点上打出更多的锚点以链接到更远的节点,但是这样整个链表将会变得非常复杂,所以我们今天将要学习一种简单,但十分有效的数据结构,跳跃表。

结构

首先盗个图看看,懒得画了

跳跃表是一个多层相互链接的链表组,其中,L1 是基础链表,他将拥有链表中的所有元素,越向上的层级会拥有更少的元素,并且最优情况是每层元素数量是上一层二分之一。当然其分布绝对是越平均越好。

搜索

跳跃表的搜索从最高层开始不断往右搜索,直到发现过了头,接着回到上一个节点 向下一个层级 继续往右知道做过头,如此反复。就像是不断的坐车坐过站,然后坐车回去,并切换线路。

其实这个东西和 AVL 非常类似,很明显带有树的一些特性,或者说就是一个棵树,跳跃的过程其实就是类似二分查找的一个过程,每次的向下切换线路都会滤过一部分的节点。

插入

插入的过程会比搜索复杂一些,首先,最底层肯定的必定插入,接下来我们使用一个 50%概率的随机数比较,如果正面,提升,去往上一级,到了上一级继续投硬币,如果正面,再次提升,如此反复,如果上层链表还不存在,生成他。

要注意的是,每一层链表的起始点都必须是一个负无穷的数。这可以避免搜索从右上角开始的问题。

实现

自己也依靠公开课中的描述以及自己的理解实现了一个跳跃表,书里好像没这章节,所以走了不少弯路,如果你们发现错误的话还请纠正一下。实际用代码实现的时候还是有些复杂的。

class Node {
  constructor(val) {
    this.val = val
    this.low = null
    this.high = null
    this.left = null
    this.right = null
    this.lr = null // left root 该节点的最左边 负无限节点
  }
}

class SkipList {
  constructor() {
    this.root = new Node(-Infinity) // 初始化 一层最左边的负无限节点
  }
  insert(val) {
    let x = this.root
    let lr = this.root
    let tmp = {} // 虚体 吃时候为了让while第一次跑起来
    while (x !== null && tmp !== null) {
      // 注意插入判断的顺序 首先判断 当前是否已经到了最后一个值 代表直接插入到最后
      // 接下来就是普通的判断 前面比目标小 后面比目标大
      if (x.right === null || (val >= x.val && val <= x.right.val)) {
        let node = new Node(val)
        // 在这一层的插入
        node.right = x.right
        x.right = node
        node.left = x
        node.lr = lr
        if (tmp.val) {
          // 如果tmp存在 并且是实体 互相链接
          node.low = tmp
          tmp.high = node
        }
        // 如果这个节点 属于是当前层级的最右节点,并且上层已经建立 无条件升级
        // 或者 投硬币成功
        if ((node.right === null && lr.high !== null) || Math.random() > 0.5) {
          // tmp会被下一层用来连接
          tmp = node
          // 如果没有上层 去往left-root 建立一层
          if (lr.high === null) {
            lr.high = new Node(-Infinity)
            lr.high.low = lr
          }
          x = lr.high
          lr = lr.high
        } else {
          tmp = null
        }
      } else {
        x = x.right
      }
    }
  }
  search(val) {
    let x = this.root
    // 首先找到左上角定点
    while (x.high !== null) {
      x = x.high
    }
    // 如果都为如果都为false就代表已经到了右下角的节点
    while (x.right !== null || x.low !== null) {
      if (x.val === val) {
        return 'yes i find'
      }
      if (x.val > val) {
        // 其实这时候已经可以返回false了 因为接下来x的val只可能越来越大
        return 'no result'
      }
      // 要找节点还在右边 继续
      if (x.right.val <= val) {
        x = x.right
      } else if (x.right.val > val) {
        // 已经坐过头了 向下一层
        x = x.low
      }
    }
    return 'no result'
  }
}

图片版

我只想说 太酷了 这些创造数据结构的人真是太帅了啊