package com.ls.hfvj;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* 跳表:实现对有序链表的类似二分查找
* 思路:<node,nodelevel>二元组共同确定了下一个候选结点candidate
* 参:
* https://en.wikipedia.org/wiki/Skip_list
* https://www.geeksforgeeks.org/skip-list-set-2-insertion/
* @author lihao
*/
public class SkipList{
private int count = 0;// 当前元素数量N
private Node dummy;// 虚拟头结点,统一操作。
// 两个关键变量:curnode、curlevl。<curnode,curlevl>二元组共同确定了下一个候选结点(待检验结点),每一步要么往右要么往下走。
public Node search(int key) {
Node curnode = dummy;
int curlevel = dummy.level;
while (curlevel != -1) {
while (curnode.successor[curlevel] != null && curnode.successor[curlevel].key < key) {// 一直向右走
curnode = curnode.successor[curlevel];
}
if (curnode.successor[curlevel] == null || curnode.successor[curlevel].key > key) {// 没有后继或后继的key大于目标,往下走
curlevel--;
} else {
return curnode.successor[curlevel];
}
}
return null;// 表示没找到指定key的值
}
// 找到每一层的前驱,对每个前驱进行类似单链表的插入操作。
public void insert(int key, int value) {
if (search(key) != null) {// 如果key已存在,则执行update操作
update(key, value);
return;
}
// 0.构造节点,维护dummy的层次
Node node = new Node(key, value, getLevel());
count++;
if (dummy.level + 1 < Math.log(count) / Math.log(2) + 1) {
dummy.successor = Arrays.copyOf(dummy.successor, dummy.successor.length + 1);
dummy.level = dummy.successor.length - 1;// 索引==元素个数-1
}
Node[] updates = new Node[node.level + 1];// 前驱列表,update[i]:层次i的前驱结点
// 1.找前驱列表,从最高处开始找(排除过程更快,若目标元素只有i层,则i+1及以上层次的前驱不需要保留)
int currentlevel = dummy.level;
Node currentNode = dummy;
while (currentlevel >= 0) {
while (currentNode.successor[currentlevel] != null && currentNode.successor[currentlevel].key < node.key) {
currentNode = currentNode.successor[currentlevel];
}
// currentNode.successor[currentlevel]==null || currentNode.successor[currentlevel].key>=node.key
if (currentlevel < updates.length) {// 需要保留
updates[currentlevel] = currentNode;
}
currentlevel--;
}
// 2.对每个前驱结点进行类似单链表的插入操作
for (int i = 0; i < updates.length; i++) {
node.successor[i] = updates[i].successor[i];// [新节点第i层的后继]是[第i层前驱结点]第i层的后继
updates[i].successor[i] = node;// [第i层前驱结点]第i层的后继是新结点
}
}
private void delete(int key) {
// 1.找前驱列表,从最高处开始找(排除过程更快,若目标元素只有i层,则i+1及以上层次的前驱不需要保留)
Node[] precessors=getPrecessors(key);
// 2.对每个前驱结点进行类型单链表的删除操作
for(int i=0;i<precessors.length;i++) {
//[第i层前驱结点第i层的后继]是[第i层前驱结点的第i层后继结点的第i层后继--[第i层前驱结点的第i层后继结点]即当前结点
Node node=precessors[i].successor[i];
precessors[i].successor[i]=node.successor[i];
node.successor[i]=null;
}
}
// 更新操作,先从结构中删除,更新后从新插入
private void update(int key, int value) {
delete(key);
insert(key, value);
}
/**
* 找指定节点的前驱列表。注意key不能重复,不然再拉链??
* @return nodes[i]为第i层的前驱,[0,n-1]
*/
private Node[] getPrecessors(int key) {
List<Node> precessors=new ArrayList<>();
Node currentnode = dummy;
int currentlevel = dummy.level;
// 0.找前驱列表,从最高处开始找,排除过程更快。
//1.找到第一个前驱(最高层前驱,由结点自己的层次决定)
while (currentlevel >= 0) {
while(currentnode.successor[currentlevel]!=null && currentnode.successor[currentlevel].key<key) {
currentnode=currentnode.successor[currentlevel];
}
if(currentnode.successor[currentlevel]!=null && currentnode.successor[currentlevel].key==key) {
precessors.add(currentnode);//找到了最高层的前驱结点---
currentlevel--;
break;
}else {
currentlevel--;
}
}
//2.以下的每层都可以找到前驱
while(currentlevel >= 0) {
while(currentnode.successor[currentlevel]!=null && currentnode.successor[currentlevel].key<key) {
currentnode=currentnode.successor[currentlevel];
}
precessors.add(currentnode);//找到了最高层的前驱结点---
currentlevel--;
}
Collections.reverse(precessors);//注意反转,维护函数语义。
return precessors.toArray(new Node[0]);
}
/**
* 获取结点的层级layer,默认为0层。第i层有1/2的几率上升到i+1层
*/
private int getLevel() {
int level = 0;
while (Math.random() > 0.5 && level < dummy.level) {
level++;
}
return level;
}
/**
* 结点类.简单描述流程没加泛型
*/
private static class Node{
private int key;
private int val;// 结点的值
private int level;// 结点的最大层级--索引[0,length-1]
private Node[] successor;// 后继结点列表
public Node(int key, int val, int maxlevel) {
this.key = key;
this.val = val;
this.level = maxlevel;
this.successor = new Node[maxlevel + 1];// [0,maxlevel],共maxlevel+1个元素。
}
}
}