跳表的由来及Java实现详解
1. 什么是跳表?
跳表(Skip List)是一种基于随机化的数据结构,用来实现有序数据的动态插入、删除和查找操作。跳表其实就是一个多层的单向链表,每一层的节点都是前一层节点的子节点,且每个节点都有概率生成更高层的后续节点。由于跳表适用于数据元素有序且动态插入、删除的情况,因此在一些高性能并发库的实现中有广泛的应用。
2. 跳表的发明历史
跳表的发明者为William Pugh,其于1990年在ACM上发表了名为Skipped List的文章,实现了一种以O(logN)时间复杂度平均的方法在跳表中进行查找、插入和删除操作,并将跳表的理论性质发扬光大地应用于同步算法的设计中。从此跳表开始受到广泛的研究和应用。
3. 跳表的Java实现
具体的Java代码实现可以参考如下的示例:
3.1 定义跳表节点类
class SkipListNode<T> {
public T value;
public SkipListNode<T>[] next;
public SkipListNode(T value, int level) {
this.value = value;
this.next = new SkipListNode[level];
}
}
SkipListNode类表示跳表的节点,包含了节点的值和跳表的前后指针,用于构建跳表的多层结构。
3.2 跳表类的定义
public class SkipList<T> {
private static final double DEFAULT_PROBABILITY = 0.5; // 节点加入新的一层的概率
private SkipListNode<T> head;
private int levelCount;
private int size;
private Random random;
private double probability;
public SkipList() {
this.head = new SkipListNode<T>(null, 0);
this.levelCount = 1;
this.size = 0;
this.random = new Random();
this.probability = DEFAULT_PROBABILITY;
}
// 查找节点
public SkipListNode<T> find(T value) {
SkipListNode<T> p = head;
for (int i = levelCount - 1; i >= 0; i--) { // 从前往后找到每层的前序节点
while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
p = p.next[i];
}
}
if (p.next[0] != null && p.next[0].value.equals(value)) { // 如果最后一层的节点是目标节点则返回
return p.next[0];
}
return null;
}
// 插入节点
public void insert(T value) {
int level = randomLevel(); // 随机生成插入节点的层数
SkipListNode<T> newNode = new SkipListNode<T>(value, level);
SkipListNode<T>[] update = new SkipListNode[level];
SkipListNode<T> p = head;
for (int i = level - 1; i >= 0; i--) { // 从前往后遍历每层,找到待插入节点的前序节点,并记录在update数组中
while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
p = p.next[i];
}
update[i] = p;
}
for (int i = 0; i < level; i++) { // 从下往上构建新节点的指针链接
newNode.next[i] = update[i].next[i];
update[i].next[i] = newNode;
}
size++;
}
// 删除节点
public void delete(T value) {
SkipListNode<T>[] update = new SkipListNode[levelCount];
SkipListNode<T> p = head;
for (int i = levelCount - 1; i >= 0; i--) { // 从上到下遍历每层,找到待删除节点的前序节点,并记录在update数组中
while (p.next[i] != null && p.next[i].value.compareTo(value) < 0) {
p = p.next[i];
}
update[i] = p;
}
if (p.next[0] != null && p.next[0].value.equals(value)) { // 如果找到了目标节点,则开始删除操作
for (int i = 0; i < levelCount; i++) { // 从上到下逐层删除
if (update[i].next[i] != null && update[i].next[i].value.equals(value)) {
update[i].next[i] = update[i].next[i].next[i];
}
}
size--;
}
}
// 随机生成新节点的层数
private int randomLevel() {
int level = 1;
while (random.nextDouble() < probability && level < 32) {
level++;
}
return level;
}
}
SkipList类表示跳表,包含了跳表的各种操作方法。find方法用于查找特定的值;insert方法用于插入新节点;delete方法用于删除节点。其中,randomLevel方法用于随机生成新节点的层数。
3.3 示例一:查找元素
public static void example1() {
SkipList<Integer> list = new SkipList<>();
for (int i = 0; i < 10; i++) { // 插入元素
list.insert(i);
}
SkipListNode<Integer> node = list.find(5); // 查找元素
System.out.println(node.value);
}
此示例中,首先构建了一个包含10个元素的跳表。然后通过调用find方法查找元素5所在的节点,并打印出节点的值。
3.4 示例二:删除元素
public static void example2() {
SkipList<Integer> list = new SkipList<>();
for (int i = 0; i < 10; i++) { // 插入元素
list.insert(i);
}
list.delete(5); // 删除元素
SkipListNode<Integer> node = list.find(5); // 查找元素
System.out.println(node);
}
此示例中,首先构建了一个包含10个元素的跳表。然后通过调用delete方法删除元素5。最后通过调用find方法查找元素5所在的节点,并打印出结果null。
4. 总结
跳表是一种基于随机化的有序数据结构,适用于动态插入、删除和查找场景,具有O(logN)时间复杂度平均的特性。Java中可以通过定义SkipListNode和SkipList类来实现跳表的基本操作,为多种场景提供了高效的数据结构支持。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:跳表的由来及Java实现详解 - Python技术站