数据结构之-跳跃表(skip list, scala版)
概述
SkipList 是由William Pugh发明的一种数据结构,它的作用类似平衡二叉树,对查找,删除,插入操作的时间复杂度为O(logN),是一种十分高效的查找结构。SkipList使用随机化的平衡方案取代了平衡二叉树的严格的平衡方案,因此它也是一种随机化的数据结构。SkipList基于并联的链表,是对有序的链表附加上随机个数的前进的链接,使得在查找过程中可以快速地跳过部分链表而得名。
如上图所示,a为普通的排好序的LinkList,在a中查找一个元素,最坏情况下,我们需要对比所有的节点,如果修改成如b那样,间隔的list中的元素加上指向前面第二个的元素指针,则查找一个元素最多需要【n/2】+ 1次对比,同理对应c来说,则需要【n/4】 + 1次,如果对任意第2^i个节点拥有i个指针来指向前面的i个节点,那么其查找效率就会降到logN,但是这种数据结构虽然可以很快的进行查找,但是对于插入和删除操作确实不切实际的。一个拥有k个向前指针的节点称为k层节点,像之前描述的结构,节点的层次分布模式为:%50为1层,25%为2层,12.5%为三层,以此类推。如果按一定比例来随机分配节点的层次的话(如图中的e),节点的第i个向前指针只需指向第i层的下一个节点。当插入一个节点时,随机的选择它的层次,并且不再需要改变。虽然有时候某些层次的安排会带来低效的执行效率,但是因为是随机化的,所以这种情况是很罕见的。
初始化
SkipList的所有层级的子list都终结于NIL(不带任何合法值的元素),刚创建的SkipList的层级为1,只有一个头结点,并且只包含一个指向NIL的向前结点。
随机化层次选择
设拥有第i个向前指针的节点,同时拥有第i+1个向前指针分布比例为p,若有一半的节点有第i个向前指针的节点,同时拥有第i+1个向前指针,则p等于1/2.则随机化的层次选择过程如下:
randomLevel() lvl := 1 -- random() that returns a random value in [0...1) while random() < p and lvl < MaxLevel do lvl := lvl + 1 return lvl
这里有个MaxLevel,它的值为 MaxLevel =L(N)= log(1/p)N (p=1/2)=log(2).N,它表示跳跃表最大允许的层次。
效率分析
设C(K)为查找第k个节点需要消耗的时间,则:
C(0) = 0 C(k) = (1–p) (在同一层查找) + p (向下一层查找)
化简得
C(k) = (1–p) (1 + C(k)) + p (1 + C(k–1)) C(k) = 1/p + C(k–1) C(k) = k/p
因为k《=MaxLevel=L(n)/p + 1/(1–p),为O(logN)
查找过程
Search(list, searchKey) x := list.eader -- loop invariant: x.key < searchKey for i := list.level downto 1 do while x.forward[i].key < searchKey do x := x.forward[i] -- x.key < searchKey <= x.forward[1].key x := x.forward[1] if x.key = searchKey then return x.value else return failure
模拟查找过程:
查找6的过程如下:
- 首先从最高层开始,发现6在 header跟6节点之间
-
然后继续降到第三层
- 还是发现6在 header跟6节点之间,降到第二层
- 第一层时,发现6在节点3和6直接,此时x指向3这个节点
-
3的下一个节点是6,刚好找到
插入过程
Insert(list, searchKey, newValue) local update[1..MaxLevel] x := list.header for i := list.level downto 1 do while x.forward[i].key < searchKey do x := x.forward[i] -- x.key < searchKey £ x.forward[i].key update[i] := x x := x.forward[1] if x.key = searchKey then x.value := newValue else lvl := randomLevel() if lvl > list.level then for i := list.level + 1 to lvl do update[i] := list.header list.level := lvl x := makeNode(lvl, searchKey, value) for i := 1 to level do x.forward[i] := update[i].forward[i] update[i].forward[i] := x
删除过程
Delete(list, searchKey) local update[1..MaxLevel] x := list.header for i := list.level downto 1 do while x.forward[i].key < searchKey do x := x.forward[i] update[i] := x x := x.forward[1] if x.key = searchKey then for i := 1 to list.level do if update[i].forward[i] != x then break update[i].forward[i] := x.forward[i] free(x) while list.level > 1 and list.header.forward[list.level] = NIL do list.level := list.level – 1
SCALA实现
package chiyx.algo.datastruct /** * Created with IntelliJ IDEA. * scala版本的跳跃表的实现 * @author qianshang@taobao.com * @since 下午11:00 13-9-2 * */ class SkipList[T <% Ordered[T]] { /** * 随机化的概率,每层节点拥有上一层指针的概率 */ private val P = 0.5 /** * 最高层级为8,则 N的合适范围在 2^^8 */ private val MaxLevel = 8 private val header: SkipNode[T] = new SkipNode[T](MaxLevel) /** * 当前的最大层级编号,从0开始编号 */ private var level = 0 /** * 随机产生插入节点的层高 * @return */ def randomLevel = { val lvl = (Math.log(1.0 - Math.random()) / Math.log(1 - P)).toInt Math.min(lvl, MaxLevel) } /** * 检查是否包含元素 * @param o * @return */ def contains(o: T) = { var x = header for (i <- level.to(0, -1)) { while (x.forward(i) != null && x.forward(i).value < o) { x = x.forward(i) } } x = x.forward(0) x != null && x.value.equals(o) } /** * 插入元素 * @param o */ def insert(o: T) { var x = header val update = new Array[SkipNode[T]](MaxLevel + 1) for (i <- level.to(0, -1)) { while (x.forward(i) != null && x.forward(i).value < o) { x = x.forward(i) } update(i) = x } x = x.forward(0) /** * 如果不存在,则创建节点 */ if (x == null || x.value != o) { val lvl = randomLevel if (lvl > level) { for (i <- level to lvl) { update(i) = header } level = lvl } x = new SkipNode[T](o, lvl) for (i <- 0 to lvl) { x.forward(i) = update(i).forward(i) update(i).forward(i) = x } } } /** * 删除操作 * @param o */ def delete(o: T) { var x = header val update = new Array[SkipNode[T]](MaxLevel + 1) for (i <- level.to(0, -1)) { while (x.forward(i) != null && x.forward(i).value < o) { x = x.forward(i) } update(i) = x } x = x.forward(0) //元素存在的情况下才需要删除 if (x != null && x.value == o) { for (i <- 0 to level) { if (update(i).forward(i) == x) { update(i).forward(i) = x.forward(i) } } while (level > 0 && header.forward(level) == null) { level = level - 1 } } } } /** * 跳跃表中的节点 * @tparam T */ private class SkipNode[T](val level: Int) { var value: T = _ /** * 指向多个层级的下个节点的指针数组 */ val forward: Array[SkipNode[T]] = new Array[SkipNode[T]](level + 1) def this(ve: T, level: Int) = { this(level) value = ve } } object SkipListApp { def main(args: Array[String]) { val sList = new SkipList[Int] sList insert 5 sList insert 4 sList insert 6 sList insert 9 println( sList contains 6) sList delete 6 println( sList contains 6) } }
下一篇: 如何在android应用中添加项目符号