Java数据结构之实现跳表,是一篇对跳表数据结构的详细讲解。
背景
跳表是一种基于有序链表的高效查找算法,它的查找时间复杂度为O(logn),相比于普通链表的O(n),具有很大的优势。本文将介绍跳表的实现过程。
实现跳表
1. 跳表结构体
跳表的数据结构体实现包含以下四项:
- 头结点head:表示链表的起始位置。
- 节点Node:跳表中的节点,包含表层链表和下层链表的节点信息。
- 游标Random:在每层链表中,上一个节点和下一个节点之间的所有节点都是此节点的后代,即从此节点出发到达下一个节点。
- 层数Level:跳表的层数,表示最高层数。
代码示例:
public class SkipList<K extends Comparable<K>, V> {
private static final double DEFAULT_PROBABILITY = 0.5;
private int level;
private final Random random = new Random();
private final Node<K, V> head = new Node<>(null, null, 0);
private double probability = DEFAULT_PROBABILITY;
...
}
2. 跳表节点实现
跳表中的节点包含以下信息:
- key:节点键值。
- value:节点保存的值。
- maxLevel:节点所在的最高层数。
- nextNodes:一个数组,保存每层链表中下一个节点的信息。
代码示例:
private static class Node<K, V> {
private final K key;
private final V value;
private final int maxLevel;
private final Node<K, V>[] nextNodes;
public Node(K key, V value, int maxLevel) {
this.key = key;
this.value = value;
this.maxLevel = maxLevel;
this.nextNodes = new Node[maxLevel];
}
}
3. 插入节点
将节点插入跳表的过程分为以下几步:
- 遍历每一层查找比要插入的节点的key大的节点。
- 如果当前所在层的节点的下一个节点的key大于输入节点的key,则向下一层继续搜索;否则在当前层的链表中插入输入节点。
代码示例:
public void insert(K key, V value) {
int level = randomLevel();
Node<K, V> newNode = new Node<>(key, value, level);
Node<K, V>[] update = new Node[level];
Node<K, V> current = head;
for (int i = level - 1; i >= 0; i--) {
while (current.nextNodes[i] != null && current.nextNodes[i].key.compareTo(key) < 0) {
current = current.nextNodes[i];
}
update[i] = current;
}
if (current.nextNodes[0] != null && current.nextNodes[0].key.equals(key)) {
// Key already exists, update value。
current.nextNodes[0].value = value;
} else {
for (int i = 0; i < level; i++) {
newNode.nextNodes[i] = update[i].nextNodes[i];
update[i].nextNodes[i] = newNode;
}
}
}
4. 查找节点
查找节点发生时,从最高层开始,并且必须保证在当前层所有的节点前面或不在当前层存在相同的val节点。
代码示例:
public V search(K key) {
Node<K, V> current = head;
for (int i = level - 1; i >= 0; i--) {
while (current.nextNodes[i] != null && current.nextNodes[i].key.compareTo(key) < 0) {
current = current.nextNodes[i];
}
}
if (current.nextNodes[0] != null && current.nextNodes[0].key.equals(key)) {
return current.nextNodes[0].value;
} else {
return null;
}
}
5. 删除节点
删除节点分为以下几个步骤:
- 遍历每一层链表,找到要删除的节点所在的位置;
- 从高层链表往下删除节点,遇到链表为空时,直接跳过当前层,到下一层执行删除操作;
- 如果要删除的节点在最底层链表中,则先删除最底层的节点,然后再从高层往下删除其它节点。
代码示例:
public void delete(K key) {
Node<K, V>[] update = new Node[level];
Node<K, V> current = head;
for (int i = level - 1; i >= 0; i--) {
while (current.nextNodes[i] != null && current.nextNodes[i].key.compareTo(key) < 0) {
current = current.nextNodes[i];
}
update[i] = current;
}
if (current.nextNodes[0] != null && current.nextNodes[0].key.equals(key)) {
for (int i = level - 1; i >= 0; i--) {
if (update[i].nextNodes[i] == null || !update[i].nextNodes[i].key.equals(key)) {
continue;
}
update[i].nextNodes[i] = update[i].nextNodes[i].nextNodes[i];
}
}
}
总结
本文通过对跳表数据结构的详细讲解,介绍了跳表的实现步骤及代码实现。跳表虽然相比于普通链表有一定的实现难度,但是其查询效率高,适合用于需要高效查找操作的情况。
本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:Java数据结构之实现跳表 - Python技术站