源文来自这里: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; |
| |
| 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; |
| } |
| |
| |
| 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; |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public void put(int k, T v) { |
| System.out.println("添加key:" + k); |
| SkipListNode<T> p = findNode(k); |
| 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; |
| |
| SkipListNode<T> z = new SkipListNode<>(k, null); |
| insertNode(p, z); |
| z.down = q; |
| q.up = z; |
| |
| q = z; |
| currentLevel++; |
| System.out.println("添加后" + this); |
| |
| } |
| size++; |
| |
| } |
| |
| |
| |
| |
| |
| |
| |
| |
| 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()); |
| } |
| } |
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律