跳跃表--Java实现

源文来自这里:https://www.cnblogs.com/shoshana-kong/p/10798489.html

原理就不多说了,网上有很多介绍的。这里直接上代码。

SkipListNode

public class SkipListNode<T> {
public int key;
public T value;
public SkipListNode<T> pre, next, up, down; //上下左右四个节点,pre和up存在的意义在于 "升层"的时候需要查找相邻节点
public static final int HEAD_KEY = Integer.MIN_VALUE; // 负无穷
public static final int TAIL_KEY = Integer.MAX_VALUE; // 正无穷
public SkipListNode(int k, T v) {
key = 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);
}
@Override
public String toString() {
return "key-value:" + key + "," + value;
}
}

SkipList

import java.util.Random;
public class SkipList<T> {
private SkipListNode<T> head, tail;
private int size;
private int listLevel;
private Random random;
private static final double PROBABILITY = 0.5;
public SkipList() {
head = new SkipListNode<>(SkipListNode.HEAD_KEY, null);
tail = new SkipListNode<>(SkipListNode.TAIL_KEY, null);
head.next = tail;
tail.pre = head;
size = 0;
listLevel = 0;
random = new Random();
}
public SkipListNode<T> get(int key) {
SkipListNode<T> p = findNode(key);
if (p.key == key) {
return p;
}
return null;
}
//首先查找到包含key值的节点,将节点从链表中移除,接着如果有更高level的节点,则repeat这个操作即可。
public T remove(int k) {
SkipListNode<T> p = get(k);
if (p == null) {
return null;
}
T oldV = p.value;
SkipListNode<T> q;
while (p != null) {
q = p.next;
q.pre = p.pre;
p.pre.next = q;
p = p.up;
}
return oldV;
}
/**
* put方法有一些需要注意的步骤:
* 1.如果put的key值在跳跃表中存在,则进行修改操作;
* 2.如果put的key值在跳跃表中不存在,则需要进行新增节点的操作,并且需要由random随机数决定新加入的节点的高度(最大level);
* 3.当新添加的节点高度达到跳跃表的最大level,需要添加一个空白层(除了-oo和+oo没有别的节点)
*
* @param k
* @param v
*/
public void put(int k, T v) {
System.out.println("添加key:" + k);
SkipListNode<T> p = findNode(k);//这里不用get是因为下面可能用到这个节点
System.out.println("找到P:" + p);
if (p.key == k) {
p.value = v;
return;
}
SkipListNode<T> q = new SkipListNode<>(k, v);
insertNode(p, q);
int currentLevel = 0;
while (random.nextDouble() > PROBABILITY) {
if (currentLevel >= listLevel) {
addEmptyLevel();
System.out.println("升层");
}
while (p.up == null) {
System.out.println(p);
p = p.pre;
System.out.println("找到第一个有上层结点的值" + p);
}
p = p.up;
//创建 q的镜像变量(只存储k,不存储v,因为查找的时候会自动找最底层数据)
SkipListNode<T> z = new SkipListNode<>(k, null);
insertNode(p, z);
z.down = q;
q.up = z;
//别忘了把指针移到上一层。
q = z;
currentLevel++;
System.out.println("添加后" + this);
}
size++;
}
/**
* 如果传入的key值在跳跃表中不存在,则findNode返回跳跃表中key值小于key,并且key值相差最小的底层节点;
* 所以不能用此方法来代替get
*
* @param key
* @return
*/
public SkipListNode<T> findNode(int key) {
SkipListNode<T> p = head;
while (true) {
System.out.println("p.next.key:" + p.next.key);
if (p.next != null && p.next.key <= key) {
p = p.next;
}
System.out.println("找到node:" + p);
if (p.down != null) {
System.out.println("node.down :" + p);
p = p.down;
} else if (p.next != null && p.next.key > key) {
break;
}
}
return p;
}
public boolean isEmpty() {
return size == 0;
}
public int size() {
return size;
}
public void addEmptyLevel() {
SkipListNode<T> p1 = new SkipListNode<T>(SkipListNode.HEAD_KEY, null);
SkipListNode<T> p2 = new SkipListNode<T>(SkipListNode.TAIL_KEY, null);
p1.next = p2;
p1.down = head;
p2.pre = p1;
p2.down = tail;
head.up = p1;
tail.up = p2;
head = p1;
tail = p2;
listLevel++;
}
private void insertNode(SkipListNode<T> p, SkipListNode<T> q) {
q.next = p.next;
q.pre = p;
p.next.pre = q;
p.next = q;
}
public int getLevel() {
return listLevel;
}
@Override
public String toString() {
SkipListNode<T> down = head.down;
while (down != null && down.down != null) {
down = down.down;
}
StringBuilder sb = new StringBuilder("SkipList{");
while (down != null && down.next != null) {
if (down.key > -1) {
sb.append("(").append(down.key).append(",").append(down.value).append("),");
}
down = down.next;
}
sb.deleteCharAt(sb.length() - 1);
sb.append("}");
return sb.toString();
}
}

测试

public class SkipListTest {
public static void main(String[] args) {
SkipList<String> list = new SkipList<>();
list.put(10, "sho");
list.put(1, "sha");
list.put(9, "na");
list.put(2, "bing");
list.put(8, "ling");
list.put(7, "xiao");
list.put(100, "你好,skiplist");
list.put(5, "冰");
list.put(6, "灵");
System.out.println("列表元素:\n" + list);
System.out.println("删除100:" + list.remove(100));
System.out.println("列表元素:\n" + list);
System.out.println("5对于的value:\n" + list.get(5).value);
System.out.println("链表大小:" + list.size() + ",深度:" + list.getLevel());
}
}
posted @   我自逍遥  阅读(99)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
点击右上角即可分享
微信分享提示