跳表面试参考

1. 跳表的用途

    常用平衡数据结果有平衡二叉树(AVL Tree,红黑树(RBTree),B+树和B-树等,但是这些数据结果都比较复杂,很多人一下都不能直接写出正确的程序,而跳表恰好相反,跳表也是一种平衡的数据结果,它的搜索效率和平衡二叉树和红黑树差不多,不过跳表操作的代码却非常简单,只要你熟悉操作普通链表,就能够快速学会操作跳表。目前的软件 Redis和LevelDB都有用到它

2.跳表的表示:

 下面的结构是就是跳表:

   其中 -1 表示 INT_MIN, 链表的最小值,1 表示 INT_MAX,链表的最大值。

  跳表面试参考_第1张图片

  跳表具有如下性质:

(1) 由很多层结构组成

(2) 每一层都是一个有序的链表

(3) 最底层(Level 1)的链表包含所有元素

(4) 如果一个元素出现在 Level i 的链表中,则它在 Level i 之下的链表也都会出现。

(5) 每个节点包含两个指针,一个指向同一链表中的下一个元素,一个指向下面一层的元素。

3.跳表的搜索:

  跳表面试参考_第2张图片

例子:查找元素 117

(1) 比较 21, 比 21 大,往后面找

(2) 比较 37,   比 37大,比链表最大值小,从 37 的下面一层开始找

(3) 比较 71,  比 71 大,比链表最大值小,从 71 的下面一层开始找

(4) 比较 85, 比 85 大,从后面找

(5) 比较 117, 等于 117, 找到了节点。

具体的搜索算法如下:

/* 如果存在 x, 返回 x 所在的节点, 
 * 否则返回 x 的后继节点 */  
find(x)   
{  
    p = top;  
    while (1) {  
        while (p->next->key < x)  
            p = p->next;  
        if (p->down == NULL)   
            return p->next;  
        p = p->down;  
    }  
}  
4.跳表的插入:

 先确定该元素要占据的层数 K(采用丢硬币的方式,这完全是随机的)

然后在 Level 1 ... Level K 各个层的链表都插入元素。

例子:插入 119, K = 2

跳表面试参考_第3张图片

如果 K 大于链表的层数,则要添加新的层

例子:插入 119, K = 4

跳表面试参考_第4张图片
插入元素的时候,元素所占有的层数完全是随机的,通过一下随机算法产生:

int random_level()  
{  
    K = 1;  
  
    while (random(0,1))  
        K++;  
  
    return K;  
}  


   相当与做一次丢硬币的实验,如果遇到正面,继续丢,遇到反面,则停止,用实验中丢硬币的次数 K 作为元素占有的层数。显然随机变量 K 满足参数为 p = 1/2 的几何分布,    K  的期望值 E[K] = 1/p = 2. 就是说,各个元素的层数,期望值是 2 层。5.跳表的高度:  n 个元素的跳表,每个元素插入的时候都要做一次实验,用来决定元素占据的层数K,跳表的高度等于这 n 次实验中产生的最大 K,待续。6.跳表的空间复杂度分析:  根据上面的分析,每个元素的期望高度为 2, 一个大小为 n 的跳表,其节点数目的期望值是 2n7.跳表的删除:  在各层中找到相应的结点,并全部分删除即可

   跳表面试参考_第5张图片8. 在Java中,jdk已经为我们封装好了跳表这个数据结果,主要实现的类有:ConcurrentSkipListMap  和  ConcurrentSkipListSet 这两类,其中ConcurrentSkipListSet是在CocurrentSkipListMap的基础上实现的,示例代码如下:

package com.tds.commons;

import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.ConcurrentSkipListSet;

/**
 * 跳表
 * @author Administrator
 *
 */
public class SkipListTest {

	public static void main(String[] args) {
		
		ConcurrentSkipListMap<String, String> skipListMap = new ConcurrentSkipListMap<String, String>();
		ConcurrentSkipListSet<String> skipListSet = new ConcurrentSkipListSet<String>();
		
		skipListSet.add("123");
		skipListSet.add("3");
		skipListSet.add("2");
		skipListSet.add("1");
		skipListSet.add("12");
		skipListSet.add("3");
		
		for (String str : skipListSet) {
			System.out.println(str);
		}
		
		skipListMap.put("123", "123");
		skipListMap.put("3", "123");
		skipListMap.put("2", "123");
		skipListMap.put("1", "123");
		skipListMap.put("12", "123");
		skipListMap.put("3", "456");
		
		for (String str : skipListMap.keySet()) {
			System.out.println(str + ": " + skipListMap.get(str));
		}
	}
}

 输出的结果如下:

1
12
123
2
3
1: 123
12: 123
123: 123
2: 123
3: 456

在数据特别大,对性能有要求的,内存空间足够的情况下,用跳表是一种不错的选择

你可能感兴趣的:(跳表面试参考)