java 跳跃表_跳跃表原理及java实现跳跃表

对于一个链表的查询和列表不同。如果列表是有序的那么可以用二分查找,时间复杂度为log n,链表即便是有序的,查询一个元素也是要从头遍历,时间复杂度为O(n) 。为了解决链表的查询效率问题,出现了一些特殊的数据结构,例如:树类的包括1、二叉树 2、平衡二叉树(理想最好情况)3、红黑树 4、b 树 5、b+ 树 等等,还有一种区别于树类型的数据结构:跳跃表。跳跃表类似多维链表,最高纬跳跃最大,最低纬就是普通的链表存储了所有的数据。例如链表 7 - 21 - 32 - 37 -71 - 85 如下图

04c79b131e3e

image.png

1、初始化先由下到上创建一个head竖向链表,Node的值为空,无穷小。

2、跳跃表的查找过程,例如查找的是节点32。红线是指针变化工程,虚线是判断过程。如图

04c79b131e3e

image.png

3、插入:首先查找到需要插入节点的最低纬链表的位置指针,并且记录每个维度最左位置的指针。然后随机生成高度。例如插入的数值为35,随机高度为4,如图

04c79b131e3e

image.png

4、性能

查找、插入和删除操作的时间复杂度都为: O(logn)

随机跳跃表表现性能也非常不错,节省了大量复杂的调节平衡树的代码。其效率与红黑树、伸展树等这些平衡树能够说相差不大。

但跳跃表还在并发环境下有优势。在并发环境下。假设要更新数据,跳跃表须要更新的部分就比較少,锁的东西也就比較少。所以不同线程争锁的代价就相对少了,而红黑树有个平衡的过程,牵涉到大量的节点,争锁的代价也就相对较高了。跳跃表的缺点是,存储空间占用相对较大一些。

5、应用

jdk 中有对跳跃表的并发实现:

ConcurrentSkipListMap

ConcurrentSkipListSet

了解过Redis的都知道,Redis有一个非常实用的数据结构:SortSet,基于它,我们能够非常轻松的实现一个Top N的应用。这个SortSet底层就是利用跳表实现的。

总结

作为一种简单的数据结构,在大多数应用中Skip lists可以取代平衡树。Skiplists算法很easy实现、扩展和改动。

Skip lists和进行过优化的平衡树有着相同高的性能 (红黑树)。

java代码实现:

import java.util.ArrayList;

import java.util.List;

import java.util.concurrent.ThreadLocalRandom;

public class SkipList> {

//最大层级

static final int MAX_LEVEL = 1 << 6;

static final int DEFAULT_LEVEL = 4;

//跳跃表 down node 的层级

int level;

//头节点

Node head;

ThreadLocalRandom random = ThreadLocalRandom.current();

public SkipList() {

this(DEFAULT_LEVEL);

}

//跳跃表的初始化

public SkipList(int level) {

this.level = level;

int i = level;

Node temp = null;

Node prev = null;

//从底部节点开始创建链表

while (i-- != 0) {

temp = new Node(null, (T) -> -1);

temp.down = prev;

prev = temp;

}

head = temp;//头节点

}

public void put(E val, C score) {

Node t = head;

//存储每个层级,插入新节点的最左边节点

List> paths = new ArrayList<>();

//1,找到需要插入的位置

while (t != null) {

//表示存在该值的点,表示需要更新该节点

if (t.score.compareTo(score) == 0) {

Entity e = t.entity;

//插入到链表尾部

e.next = new Entity(val, null);

return;

}

//查找插入的位置

if (t.next == null || t.next.score.compareTo(score) > 0) {

paths.add(t);

t = t.down;

} else {

t = t.next;

}

}

//随机生成新节点的层级

int lev = randomLevel();

// 如果随机的层级数,大于head节点的层级数

// 需要更新head这一列的节点数量,保证head列数量最多

if (lev > level) {

Node temp = null;

Node prev = head;

while (level++ != lev) {

temp = new Node(null, (T) -> -1);

paths.add(0, temp);

temp.down = prev;

prev = temp;

}

head = temp;

//更新层级

level = lev;

}

//从后向前遍历path中的每一个节点,在其后面增加一个新的节点

Node down = null, newNade = null, path = null;

Entity e = new Entity<>(val, null);

for (int i = level - 1; i >= level - lev; i--) {

//创建新的节点

newNade = new Node(e, score);

path = paths.get(i);

newNade.next = path.next;

path.next = newNade;

newNade.down = down;

down = newNade;

}

}

/**

* 查找跳跃表中的一个值

*/

public List get(C score) {

Node t = head;

while (t != null) {

if (t.score.compareTo(score) == 0) {

return t.entity.toList();

}

if (t.next == null || t.next.score.compareTo(score) > 0) {

t = t.down;

} else {

t = t.next;

}

}

return null;

}

/**

* 查找跳跃表中指定范围的值

*/

public List find(final C begin, C and) {

Node t = head;

Node beginNode = null;

//确定下界索引

while (t != null) {

beginNode = t;

if (t.next == null || t.next.score.compareTo(begin) >= 0) {

t = t.down;

} else {

t = t.next;

}

}

//循环链表

List list = new ArrayList<>();

for (Node n = beginNode.next; n != null; n = n.next) {

if (n.entity != null && n.score.compareTo(and) < 0)

list.addAll(n.entity.toList());

}

return list;

}

/**

* 根据score的值来删除节点。

*/

public void delete(C score) {

//1,查找到节点列的第一个节点的前驱

Node t = head;

while (t != null) {

if (t.next == null || t.next.score.compareTo(score) > 0) {

//向下查找

t = t.down;

} else if (t.next.score.compareTo(score) == 0) {

// 在这里说明找到了该删除的节点

t.next = t.next.next;

//向下查找

t = t.down;

} else {

//向右查找

t = t.next;

}

}

}

/**

* 高度随机增加一

*/

private int randomLevel() {

int lev = 1;

//产生一个非负整数

while (random.nextInt() % 2 == 0)

lev++;

return Math.min(lev, MAX_LEVEL);

}

/**

* 跳跃表的节点链表

*/

static class Node {

Entity entity;//存储的数据

C score; //需要排序的字段

Node next;//指向下一个节点的指针

Node down;//指向下一层的指针

Node(Entity entity, C score) {

this.entity = entity;

this.score = score;

}

@Override

public String toString() {

return "Node{score=" + score + '}';

}

}

static class Entity {

E val;

Entity next;

Entity(E val, Entity next) {

this.val = val;

this.next = next;

}

List toList() {

List list = new ArrayList<>();

list.add(val);

for (Entity e = next; e != null; e = e.next) {

list.add(e.val);

}

return list;

}

@Override

public String toString() {

return "Entity{" + "val=" + val + '}';

}

}

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值