增加了向前指针的链表叫作跳表。跳表全称叫做跳跃表,简称跳表。跳表是一个随机化的数据结构,实质就是一种可以进行二分查找的有序链表。跳表在原有的有序链表上面增加了多级索引,通过索引来实现快速查找。跳表不仅能提高搜索性能,同时也可以提高插入和删除操作的性能。-----来自百度百科
它的效率和红黑树以及 AVL 树不相上下,但实现起来比前面两个要简单得多、
跳表具有非常优秀的查找、插入、删除性能,并且是天然的动态数据结构。
其查询、插入、删除的时间复杂度均为O(logn)
跳表的引入
跳表的查找
跳表的查找需要从头开始查找;如果key小于或等于当层后面节点的key,则继续向后查找;如果key更大,则层数减1即向下查找,继续比较。最终一定会到第一层
/*
* @param: [value]
* @return: DataStructure.SkipList.SkipList.Node<V>
* @author: Jerssy
* @dateTime: 2021/4/9 16:56
* @description: 查找value
*/
private Node<V> searchNode(V value){
Node<V> headNode=head;
while (headNode!=null){
while (headNode.next != null&&headNode.next.value.compareTo(value)<0)
headNode = headNode.next;
if (headNode.down==null){
return headNode.next;
}
headNode=headNode.down;
}
return null;
}
跳表的插入
跳表的插入首先需要确定插入的层级,层级一般使用随机函数来确定
// 随机函数
private int randomLevel(){
int k = 1;
while(random.nextInt()%2 == 0){
k ++;
}
return Math.min(k, MAX_LEVEL);
}
比如计算随机函数的值为2则1层和2层都需要插入该值,这里我们需要使用集合list保存每层插入的位置-- 从顶层开始,逐层找到每层需要插入的位置并保存位置,再插入;当然如果随机的层级数大于了当前跳表的层级数则需要更新层级数
/*
* @param: [value]
* @return: void
* @author: Jerssy
* @dateTime: 2021/4/9 16:56
* @description:插入value
*/
private void insertNode( V value){
Node<V> tempNode=head;
List<Node<V>> insertedLeveList=new ArrayList<>();
while (tempNode!=null){
//将每个层级需要插入的位置保存到集合中
if (tempNode.next==null||tempNode.next.value.compareTo(value)>0){
insertedLeveList.add( tempNode);
tempNode=tempNode.down;
}
else {
tempNode=tempNode.next;
}
}
int level = randomLevel();
// 如果随机的层级数,大于head节点的层级数
if (level > this.level) {
Node<V> temp = null;
Node<V> prev = head;
// 索引的层级数
while (this.level++ != level) {
temp = new Node<>(null);
temp.level=this.level;
insertedLeveList.add(0, temp);
temp.down = prev;
prev = temp;
}
head = temp;
//更新层级
this.level = level;
}
Node<V> downNode=null; Node<V> vNode ;Node<V> newNode;
for (int i = this.level-1;i>=this.level-level;i--) {//level的每一层都需要插入newNode
newNode=new Node<>(value);
vNode =insertedLeveList.get(i);
newNode.next=vNode.next;
newNode.level=vNode.level;
vNode.next = newNode;
newNode.down=downNode;
downNode=newNode;
}
}
跳表的删除
删除则更简单,从跳表头部开始查询删除的位置,如果比删除值小则继续向后查找,反之向下查找删除的位置,如果找到则将当前指针断裂,当前位置的前一个指针连接后面一个即可
继续向下层寻找。因为每个层级的都需要删除
注意:
因为单链表中的删除操作需要拿到要删除结点的前驱结点,然后通过
指针操作完成删除。所以在查找要删除的结点的时候,一定要获取前驱结点。当然,如果我们用的是双向链表,就不需要考虑这个问题了。
/*
* @param: [value]
* @return: void
* @author: Jerssy
* @dateTime: 2021/4/9 16:55
* @description: 删除value
*/
private void deleteNode(V value){
if (value == null) {
return;
}
Node<V> headNode=head;
while (headNode != null) {
//因为此跳表基于单链表,则需要找到删除的前驱进行指针的连接,如果是双链表的则直接定位到删除节点
if (headNode.next!=null&&headNode.next.value.compareTo(value)==0){
headNode.next=headNode.next.next;
headNode=headNode.down;//继续向下层寻找--每个层级的都需要删除
}
else if (headNode.next!=null&&headNode.next.value.compareTo(value)<0) headNode=headNode.next;
else headNode=headNode.down;
}
}
跳表模拟
跳表本身就是轻量级的数据结构,直接使用单链表进行模拟,当前也可以使用数组来模拟这里不再实现
package DataStructure.SkipList;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author: Jerssy
* @create: 2021-04-08 19:55
* @version: V1.0
* @slogan: 业精于勤, 荒于嬉;行成于思,毁于随。
* @description: 跳表实现
*/
public class SkipList<V extends Comparable<V>> {
private Node<V> head=new Node<>(null);
public int level;
public static final int MAX_LEVEL=16;
ThreadLocalRandom random = ThreadLocalRandom.current();
public SkipList(int level){
this.level = level;
initList();
}
public SkipList(){
this(MAX_LEVEL);
}
/*
* @param: []
* @return: void
* @author: Jerssy
* @dateTime: 2021/4/9 16:56
* @description: 初始化
*/
private void initList() {
int i = level;
Node<V> temp = null;
Node<V> prev = null;
//从底部节点开始创建链表每个层级的头节点
while (i-- != 0) {
temp = new Node<>(null);
temp.level= level-i;
temp.down = prev;
prev = temp;
}
head = temp;//最上层头节点
}
/*
* @param: [value]
* @return: DataStructure.SkipList.SkipList.Node<V>
* @author: Jerssy
* @dateTime: 2021/4/9 16:56
* @description: 查找value
*/
private Node<V> searchNode(V value){
Node<V> headNode=head;
while (headNode!=null){
while (headNode.next != null&&headNode.next.value.compareTo(value)<0)
headNode = headNode.next;
if (headNode.down==null){
return headNode.next;
}
headNode=headNode.down;
}
return null;
}
// 随机函数
private int randomLevel(){
int k = 1;
while(random.nextInt()%2 == 0){
k ++;
}
return Math.min(k, MAX_LEVEL);
}
/*
* @param: [value]
* @return: void
* @author: Jerssy
* @dateTime: 2021/4/9 16:56
* @description:插入value
*/
private void insertNode( V value){
Node<V> tempNode=head;
List<Node<V>> insertedLeveList=new ArrayList<>();
while (tempNode!=null){
//将每个层级需要插入的位置保存到集合中
if (tempNode.next==null||tempNode.next.value.compareTo(value)>0){
insertedLeveList.add( tempNode);
tempNode=tempNode.down;
}
else {
tempNode=tempNode.next;
}
}
int level = randomLevel();
// 如果随机的层级数,大于head节点的层级数
if (level > this.level) {
Node<V> temp = null;
Node<V> prev = head;
// 索引的层级数
while (this.level++ != level) {
temp = new Node<>(null);
temp.level=this.level;
insertedLeveList.add(0, temp);
temp.down = prev;
prev = temp;
}
head = temp;
//更新层级
this.level = level;
}
Node<V> downNode=null; Node<V> vNode ;Node<V> newNode;
for (int i = this.level-1;i>=this.level-level;i--) {//level的每一层都需要插入newNode
newNode=new Node<>(value);
vNode =insertedLeveList.get(i);
newNode.next=vNode.next;
newNode.level=vNode.level;
vNode.next = newNode;
newNode.down=downNode;
downNode=newNode;
}
}
/*
* @param: [value]
* @return: void
* @author: Jerssy
* @dateTime: 2021/4/9 16:55
* @description: 删除value
*/
private void deleteNode(V value){
if (value == null) {
return;
}
Node<V> headNode=head;
while (headNode != null) {
//因为此跳表基于单链表,则需要找到删除的前一个进行指针的连接,如果是双链表的则直接定位到删除节点
if (headNode.next!=null&&headNode.next.value.compareTo(value)==0){
headNode.next=headNode.next.next;
headNode=headNode.down;//继续向下层寻找--每个层级的都需要删除
}
else if (headNode.next!=null&&headNode.next.value.compareTo(value)<0) headNode=headNode.next;
else headNode=headNode.down;
}
}
// 显示 表中的结点
public void printNode(){
Node<V> p = head;
while (p.down!=null){
p=p.down;
}
while(p.next!= null){
p = p.next;
System.out.println("打印第一层数据:"+p+ " ");
}
}
static class Node<V extends Comparable<V>>{
V value;
Node<V> next;
Node<V> down;
int level;
public Node(V value){
this.value = value;
}
@Override
public String toString() {
return "Node{" +
"value=" + value +
", level=" + level +
'}';
}
}
public static void main(String[] args) {
SkipList<Integer> skipList=new SkipList<>(4);
skipList.insertNode(2);
skipList.insertNode(4);
skipList.insertNode(7);
skipList.insertNode(1);
skipList.insertNode(0);
skipList.insertNode(12);
skipList.insertNode(3);
skipList.insertNode(5);
skipList.printNode();
System.out.println("************删除5**********");
skipList.deleteNode(5);
skipList.printNode();
System.out.println("************查找2**********");
System.out.println(skipList.searchNode(2));
}
}