skiplist

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个元素。
		}
	}
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值