跳跃表(Skip list)原理与java实现

转载自 【算法导论33】跳跃表(Skip list)原理与java实现

Skip list是一个用于有序元素序列快速搜索的数据结构,由美国计算机科学家William Pugh发明于1989年。它的效率和红黑树以及 AVL 树不相上下,但实现起来比较容易。作者William Pugh是这样介绍Skip list的: 
Skip lists are a probabilistic data structure that seem likely to supplant balanced trees as the implementation method of choice for many applications. Skip list algorithms have the same asymptotic expected time bounds as balanced trees and are simpler, faster and use less space. 
Skip list是一个“概率型”的数据结构,可以在很多应用场景中替代平衡树。Skip list算法与平衡树相比,有相似的渐进期望时间边界,但是它更简单,更快,使用更少的空间。 
Skip list是一个分层结构多级链表,最下层是原始的链表,每个层级都是下一个层级的“高速跑道”。 
这里写图片描述 
跳跃的表的性质包括: 
某个i层的元素,出现在i+1层的概率p是固定的,例如常取p=1/2或p=1/4; 
平均来讲,每个元素出现在1/(1-p)个链表中; 
最高的元素,例如head通常采用Int.MIN_VALUE作为的最小值,会出现在每一层链表中; 
原始的链表元素如果是n,则链表最多这里写图片描述层。例如,p=1/2时,层数为这里写图片描述。 
跳跃表的空间复杂度为O(n),插入删除的时间复杂度是这里写图片描述。例如,p=1/2时,复杂度为这里写图片描述

相关资料

Skip list的维基百科; 
《算法导论》网易公开课Skip list。

跳跃表的构建

这里描述一下网易公开课《算法导论》“跳跃表”这一节的内容。 
空的跳跃表头尾相连的双向链表。

这里写图片描述
向链表中放入key-value,假如key是1。 
这里写图片描述
此时,不断投掷硬币,如果是反面,则不提升该节点,停止投掷硬币;否则不断提升该节点,并且垂直方向进行节点连接。 
这里写图片描述
如果上一步没有提升,再插入key-value,key等于2,然后不断投掷硬币,发现是投了一次正面,需要提升一次。但第二次投的是反面,不再提升。 
这里写图片描述
再插入key-value,key等于3,然后不断投掷硬币,发现第一次就投了反面,不提升。 
这里写图片描述
这样的规则一直持续下去。由于连续投正面的概率是0.5,0.5*0.5……,所以某一个节点提升很多层的概率是很低的。这也是为什么说跳跃表是一种概率型数据结构的来源。 
跳跃表的查询也比较简单。例如要查找key是3的节点,则从最上层开始查找,直到找到大于或等于3的位置,然后返回上一个节点,再往下一层继续向右寻找。例如三层的跳跃表查询路径如下。 
这里写图片描述
这样,就跳过了很多节点,所以叫做“跳跃表”。

Java实现

这里参考了“跳跃表(Skip List)-实现(Java)”,将其更改为模版形式,并多处进行了重构。 

节点类

/*** 跳跃表的节点,包括key-value和上下左右4个指针* created by 曹艳丰,2016-08-14* 参考:http://www.acmerblog.com/skip-list-impl-java-5773.html* */
public class SkipListNode <T>{public int key;public T value;public SkipListNode<T> up, down, left, right; // 上下左右 四个指针public static final int HEAD_KEY = Integer.MIN_VALUE; // 负无穷public static final int  TAIL_KEY = Integer.MAX_VALUE; // 正无穷public SkipListNode(int k,T v) {// TODO Auto-generated constructor stubkey = k;value = v;}public int getKey() {return key;}public void setKey(int key) {this.key = key;}public T getValue() {return value;}public void setValue(T value) {this.value = value;}public boolean equals(Object o) {if (this==o) {return true;}if (o==null) {return false;}if (!(o instanceof SkipListNode<?>)) {return false;}SkipListNode<T> ent;try {ent = (SkipListNode<T>)  o; // 检测类型} catch (ClassCastException ex) {return false;}return (ent.getKey() == key) && (ent.getValue() == value);}@Overridepublic String toString() {// TODO Auto-generated method stubreturn "key-value:"+key+"-"+value;}
}

跳跃表实现。

import java.util.Random;/*** 不固定层级的跳跃表* created by 曹艳丰,2016-08-14* 参考:http://www.acmerblog.com/skip-list-impl-java-5773.html* */
public class SkipList <T>{private SkipListNode<T> head,tail;private int nodes;//节点总数private int listLevel;//层数private Random random;// 用于投掷硬币private static final double PROBABILITY=0.5;//向上提升一个的概率public SkipList() {// TODO Auto-generated constructor stubrandom=new Random();clear();}/***清空跳跃表* */public void clear(){head=new SkipListNode<T>(SkipListNode.HEAD_KEY, null);tail=new SkipListNode<T>(SkipListNode.TAIL_KEY, null);horizontalLink(head, tail);listLevel=0;nodes=0;}public boolean isEmpty(){return nodes==0;}public int size() {return nodes;}/*** 在最下面一层,找到要插入的位置前面的那个key* */private SkipListNode<T> findNode(int key){SkipListNode<T> p=head;while(true){while (p.right.key!=SkipListNode.TAIL_KEY&&p.right.key<=key) {p=p.right;}if (p.down!=null) {p=p.down;}else {break;}}return p;}/*** 查找是否存储key,存在则返回该节点,否则返回null* */public SkipListNode<T> search(int key){SkipListNode<T> p=findNode(key);if (key==p.getKey()) {return p;}else {return null;}}/*** 向跳跃表中添加key-value* * */public void put(int k,T v){SkipListNode<T> p=findNode(k);//如果key值相同,替换原来的vaule即可结束if (k==p.getKey()) {p.value=v;return;}SkipListNode<T> q=new SkipListNode<T>(k, v);backLink(p, q);int currentLevel=0;//当前所在的层级是0//抛硬币while (random.nextDouble()<PROBABILITY) {//如果超出了高度,需要重新建一个顶层if (currentLevel>=listLevel) {listLevel++;SkipListNode<T> p1=new SkipListNode<T>(SkipListNode.HEAD_KEY, null);SkipListNode<T> p2=new SkipListNode<T>(SkipListNode.TAIL_KEY, null);horizontalLink(p1, p2);vertiacallLink(p1, head);vertiacallLink(p2, tail);head=p1;tail=p2;}//将p移动到上一层while (p.up==null) {p=p.left;}p=p.up;SkipListNode<T> e=new SkipListNode<T>(k, null);//只保存key就okbackLink(p, e);//将e插入到p的后面vertiacallLink(e, q);//将e和q上下连接q=e;currentLevel++;}nodes++;//层数递增}//node1后面插入node2private void backLink(SkipListNode<T> node1,SkipListNode<T> node2){node2.left=node1;node2.right=node1.right;node1.right.left=node2;node1.right=node2;}/*** 水平双向连接* */private void horizontalLink(SkipListNode<T> node1,SkipListNode<T> node2){node1.right=node2;node2.left=node1;}/*** 垂直双向连接* */private void vertiacallLink(SkipListNode<T> node1,SkipListNode<T> node2){node1.down=node2;node2.up=node1;}/*** 打印出原始数据* */@Overridepublic String toString() {// TODO Auto-generated method stubif (isEmpty()) {return "跳跃表为空!";}StringBuilder builder=new StringBuilder();SkipListNode<T> p=head;while (p.down!=null) {p=p.down;}while (p.left!=null) {p=p.left;}if (p.right!=null) {p=p.right;}while (p.right!=null) {builder.append(p);builder.append("\n");p=p.right;}return builder.toString();}}

下面进行一下测试。

public class Main {public static void main(String[] args) {// TODO Auto-generated method stubSkipList<String> list=new SkipList<String>();System.out.println(list);list.put(2, "yan");list.put(1, "co");list.put(3, "feng");list.put(1, "cao");//测试同一个key值list.put(4, "曹");list.put(6, "丰");list.put(5, "艳");System.out.println(list);System.out.println(list.size());}
}

Java中的跳跃表

Java API中提供了支持并发操作的跳跃表ConcurrentSkipListSet和ConcurrentSkipListMap。下面摘录”Java多线程(四)之ConcurrentSkipListMap深入分析“中的一些结论。 
有序的情况下: 
 在非多线程的情况下,应当尽量使用TreeMap(红黑树实现)。 
 对于并发性相对较低的并行程序可以使用Collections.synchronizedSortedMap将TreeMap进行包装,也可以提供较好的效率。但是对于高并发程序,应当使用ConcurrentSkipListMap。 
无序情况下: 
 并发程度低,数据量大时,ConcurrentHashMap 存取远大于ConcurrentSkipListMap。 
 数据量一定,并发程度高时,ConcurrentSkipListMap比ConcurrentHashMap效率更高。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/331048.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

简单的hibernate环境搭建、自动生成model/配置/hibernate.xml配置文件

自己亲测的东西才是最有效果的&#xff0c;下面贴出整个编写的过程。 1 hibernate环境搭建&#xff0c;这个博客非常给力&#xff1a;http://www.111cn.net/wy/js-ajax/93142.htm 需要用到的jar包&#xff1a; 2 使用myeclipse自动生成model/model配置文件/hibernate.xml配…

java_advanced_review(3)补充:利用网络套接字实现类似qq 的控制台通讯

【0】README1&#xff09;本文旨在实现 利用网络套接字实现类似qq 的控制台通讯&#xff0c; client 采用telnet and java app&#xff0c; server采用 java app&#xff1b;2&#xff09;also&#xff0c; you can check out the source code from https://github.com/pacoson…

Java 常见的 30 个误区与细节

转载自 Java 常见的 30 个误区与细节1、在Java中&#xff0c;没有goto语句。因为大量使用goto语句会降低程序的可读性和可维护性&#xff0c;所以Java语言取消了goto的使用。同时&#xff0c;为了避免程序员自行使用goto所带来的混乱&#xff0c;Java语言仍将goto定义为一个关键…

编译报错+解决方法:错误: 找不到符号

【0】README1&#xff09;本文主要解决“错误&#xff0c;找不到符号”【1】 console error info1&#xff09;error : E:\bench-cluster\mybatis_in_action_eclipse\xmpp\src>javac com/communication/qq/ServerPlainTest.java com\communication\qq\ServerPlainTest.java:…

干货|基于 Spring Cloud 的微服务落地

转载自 干货&#xff5c;基于 Spring Cloud 的微服务落地微服务架构模式的核心在于如何识别服务的边界&#xff0c;设计出合理的微服务。但如果要将微服务架构运用到生产项目上&#xff0c;并且能够发挥该架构模式的重要作用&#xff0c;则需要微服务框架的支持。在Java生态圈&…

sqlserver 2005 数据库的差异备份与还原

找到一个可靠的步骤&#xff0c;点开链接&#xff1a;http://blog.csdn.net/kevindr/article/details/22154323

gradle 构建 Smack 报错:Can't find android.jar for 8 API. + steps for building android platform

【0】README1&#xff09;本文旨在解决 Cant find android.jar for 8 API. Please install corresponding SDK platform package&#xff1b;2&#xff09;解决方法&#xff1a;在 Smack-master 的子目录下 有一个 version.gradle&#xff0c; 这也是 build.gradle 文件引入的一…

Hibernate的关联映射--一对多、

这是我 1 单向一对多&#xff1a; 实体类&#xff1a;&#xff08;课程类&#xff09;Grade与&#xff08;学生类&#xff09;Student的一对多关系 学生类&#xff1a; public class Student implements java.io.Serializable {// Fieldsprivate Long id;private String na…

史上最全 BAT 大厂面试题整理

转载自 史上最全 BAT 大厂面试题整理&#xff01;&#xff08;速度收藏&#xff09;主要分为以下几部分&#xff1a;&#xff08;1&#xff09;java面试题&#xff08;2&#xff09;Android面试题&#xff08;3&#xff09;高端技术面试题&#xff08;4&#xff09;非技术性问题…

微信JS-SDK实现分享功能

1 申请一个微信公众号&#xff0c;并确认在开发–接口权限中拥有分享功能的权限。 2 公众号设置–功能设置&#xff1a;在JS接口安全域名中添加安全域名&#xff0c;这个安全域名不是url&#xff0c;只需添加一级域名即可。 3 开发&#xff0c;基本配置中&#xff0c;需要获得…

通过smack client + openfire server 实现 peer to peer communication

【0】README1&#xff09;本文旨在 给出源代码 实现 smack client openfire server 实现 peer to peer communication2&#xff09;当然&#xff0c;代码中用到的 user 和 pass&#xff0c; 你需要事先在 openfire 里面注册&#xff1b;3&#xff09;also , you can checkout …

Java 面试题经典 77 问

转载自 Java 面试题经典 77 问&#xff08;含答案&#xff09;&#xff01;金三银四了&#xff0c;3月底&#xff0c;4月初&#xff0c;找工作换单位的黄金时期。4月初将会有有一大批职场人士流动。。。 作为Java开发码农的你是不是也在蠢蠢欲动&#xff0c;或者已经搞了几轮车…

静态工具类注入service的方法

http://blog.sina.com.cn/s/blog_6e2d53050102wl3x.html

阿里面试回来,想和 Java 程序员谈一谈

转载自 阿里面试回来&#xff0c;想和 Java 程序员谈一谈&#xff01; 第一个问题&#xff1a;阿里面试都问什么&#xff1f; 这个是让LZ最头疼的一个问题&#xff0c;也是群里的猿友们问的最多的一个问题。说实话&#xff0c;LZ只能隐约想起并发、JVM、分布式、TCP/IP协议这…

关于 tomcat启动后无法访问的问题(localhost:8080 (or your port specified))

【0】README1&#xff09;启动 tomcat7&#xff0c; 然后通过 localhost:8080 无法访问&#xff0c;我也是醉了&#xff1b;以下给出了解决方法&#xff1b;【1】解决方法&#xff08;如何让 Server Locations 和 deploye path 成为可编辑区域&#xff09;step1&#xff09;首…

eclipse运行报java.lang.OutOfMemoryError: PermGen space解决方法

分享一个亲测的网址&#xff1a; http://blog.csdn.net/cuker919/article/details/45246073

百度三轮面试回来,想和Java程序员分享一下。

转载自 百度三轮面试回来&#xff0c;想和Java程序员分享一下。一&#xff0c;百度一面1、给一个函数&#xff0c;返回 0 和 1&#xff0c;概率为 p 和 1-p&#xff0c;请你实现一个函数&#xff0c;使得返回 01 概率一样。 2、10 亿个 url&#xff0c;每个 url 大小小于 56B&a…

springmvc sends and receives data by ajax request using json format

【0】README 1) springmvc sends or receives data by ajax request using json format; 【1】 ajax function <script type"text/javascript">var checkoutUserlistIsAccess false;$(document).ready(function(){ $("#first").hide();if(checkout…

在Java中如何设置一个定时任务,在每天的一个时间点自动执行一个特定的程序

Quartz定时机制 首先导入jar包到程序内 quartz-all-1.6.0.jar 然后创建一个XML TimeConfig.xml 名字可以自己定义 <?xml version"1.0" encoding"UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.sp…

程序员如何面对 HR 面试的 40 个问题

转载自 程序员如何面对 HR 面试的 40 个问题&#xff01;讲一个身边朋友亲身经历的故事吧。 一个技术非常牛的朋友去阿里面试&#xff0c;成功通过了几轮技术车轮战&#xff0c;最后躺在了 HR 面上。。。所以&#xff0c;尽管你技术再牛逼&#xff0c;你回答不好 HR 的问题&…