跳表的java实现,转载自网络,仅供自己学习使用
文档结构:
1、名词解释
2、代码结构
2、代码实现
1、跳表是什么
跳表(Skip List)是一种基于有序链表的数据结构,设计目的是为了在保持链表结构的同时,通过多级索引实现快速的查找、插入和删除操作。跳表使用了分层的索引,使得查找效率大大提高,接近于平衡二叉树的效率,但实现起来比平衡树要简单得多。
跳表的结构
跳表由多个层次组成,每一层都是一个链表,并且每一层的链表都包含上一层的一部分节点。最底层是一个包含所有元素的普通链表,而在上层的链表中,节点的数量逐渐减少,通常是上一层节点数的一半。每一层的节点通过指向下层节点的指针来构成层间的跳跃关系。
跳表的操作
-
查找:跳表的查找过程类似于二分查找,但它是通过多级索引来加速查找过程的。查找时,从最高层的链表开始遍历,每次根据节点值选择合适的方向,逐层跳跃直到最低层,然后在最低层执行线性查找。
-
插入:插入一个新节点时,首先将它插入到最底层的链表中。然后根据一定的概率决定是否将该节点提升到上层链表中。每提升一层,概率逐渐减小,通常选择一半的概率来决定是否提升。
-
删除:删除操作首先查找目标节点,然后在每一层上将对应的指针删掉,最后删除底层链表中的节点。
跳表的优点
- 高效的查找:跳表的查找时间复杂度是O(log n),类似于平衡二叉树,但其实现简单得多。
- 动态:跳表能够动态地适应插入和删除操作,性能不受结构变化的影响。
- 易于实现:相较于平衡二叉树(如AVL树或红黑树),跳表的实现更为简单。
跳表的缺点
- 空间复杂度高:跳表需要多层链表结构,每层需要额外的指针来维持层间的关系,因此它的空间复杂度比普通链表高。
- 性能不如平衡树:尽管跳表的查找、插入、删除的时间复杂度是O(log n),但是在一些极端情况下,平衡树的性能可能会更好。
应用场景
跳表在需要高效查找、插入和删除操作的场景中广泛使用,尤其适合那些数据动态变化的场景,如分布式系统中的数据库索引、内存存储结构等。
总之,跳表提供了一个在理论上接近平衡二叉树的性能,同时在实现上更加简洁的替代方案。
2、代码结构
节点类:
String key 键值 对跳跃表的操作都是根据键值进行的
Int value 实际值
Node up,down,left,right; 每个节点都有四个方向
String tou;
String wei; 每层链表的头和尾节点
跳跃表类:
Head 头节点
Tail 尾结点
H 层数
Size 元素个数
Random 随机数,用来确定需不需要增加层数 即:掷硬币
findF () 按从小到大的顺序找到应该插入的位置 插入排序法
Add () 添加节点函数,在最底层插入结点后,进行掷硬币来确定是否需要增加层数,直到掷硬币不能增加层数为止,增加层数的同时需要把增加之后的节点进行连接。
Find() 根据跳跃表进行查找并打印路线。查找从最上层开始然后找到被查找节点的前个节点小于被查找节点,然后被查找节点的后一个节点大于其被查找节点,则从被查找节点的前一个节点向下走down,然后继续向右查找,直到找到为止。
2、代码实现
- package 跳跃表;
- import java.util.*;
- public class SkipList {
- public Node head; //头节点
- public Node tail; //尾结点
- public int h; //层数
- public int size; //元素个数
- public Random rand; //每次的随机数用来确定需不需要增加层数
- public SkipList(){
- Node p1 = new Node(Node.tou,0);
- Node p2 = new Node(Node.wei, 0);
- head=p1;
- tail=p2;
- head.setRight(tail);
- tail.setLeft(head);
- h=0;
- size=0;
- rand = new Random();
- }
- public boolean isEmpty(){
- if(size==0){
- return true;
- }
- return false;
- }
- //找到需要插入位置的前一个节点
- public Node findF(String k){
- Node temp;
- temp=head;
- while(true){
- while(temp.getRight().key!=Node.wei&&temp.getRight().key.compareTo(k)<=0){
- /*
- * 当链表最底层不为空的时候,从当前层向尾部方向开始查找,直到查找temp.getRight的下一个值大于 当前k的值为止,此时temp小于或等于当前k的值
- * 要插入的位置即为temp之后的位置了
- */
- temp=temp.getRight();
- }
- if(temp.getDown()!=null){
- temp=temp.getDown();
- }else{
- break;
- }
- }
- return temp; //找到节点并返回
- }
- public int add(String k, int v){
- Node temp, temp1;
- temp=findF(k);
- int i; //当前层数
- if(k.equals(temp.getKey())){
- System.out.println("对象属性完全相同无法添加!");
- int a=temp.value;
- temp.value=v;
- return a;
- }
- temp1=new Node(k,v);
- temp1.setLeft(temp);
- temp1.setRight(temp.getRight());
- temp.getRight().setLeft(temp1);
- temp.setRight(temp1);
- i=0;
- while(rand.nextDouble()<0.5){ //进行随机,是否需要 在上层添加
- if(i>=h){ //若当前层数超出了高度,则需要另建一层
- Node p1 ,p2 ;
- h=h+1;
- p1=new Node(Node.tou,0);
- p2=new Node(Node.wei,0);
- p1.setRight(p2);
- p1.setDown(head);
- p2.setLeft(p1);
- p2.setDown(tail);
- head.setUp(p1);
- tail.setUp(p2);
- head=p1;
- tail=p2;
- }
- while(temp.getUp() == null){
- temp=temp.getLeft();
- }
- temp=temp.getUp();
- Node node=new Node(k,v);
- node.setLeft(temp);
- node.setRight(temp.getRight());
- node.setDown(temp1);
- temp.getRight().setLeft(node);
- temp.setRight(node);
- temp1.setUp(node);
- temp1=node;
- i=i+1;
- }
- size=size+1;
- return 0;
- }
- //节点查找
- public Node find(String k){
- Node temp=head;
- Node node;
- node=temp;
- System.out.println("查找路线"); //用于测试
- while(temp!=null){
- while(node.getRight().key!=Node.wei&&node.getRight().getKey().compareTo(k)<=0){//&&node.getRight().getValue()!=v
- node=node.getRight();
- System.out.print("--->"+node.getKey());
- }
- if(node.getDown()!=null){
- node=node.getDown();
- System.out.print("--->"+node.getKey());
- }else{
- if(node.key.equals(k)){//&&node.getRight().value==v
- //node.setValue(111111111); //修改
- System.out.println("--->"+node.getKey());
- System.out.print("--->"+node.getValue());
- return node;
- }
- return null;
- }
- }
- return null;
- }
- //节点删除
- public void delNode(String k){ //调用查找函数,删除最底层的某个节点,并把其节点的左右相连,和链表操作一样,只是其上方若有则都需要调整
- Node temp=find(k);
- while(temp!=null){
- temp.getLeft().setRight(temp.getRight());
- temp.getRight().setLeft(temp.getLeft());
- temp=temp.getUp();
- }
- }
- public void print(){
- Node node;
- Node node1=head;
- while(node1!=null){
- int k=0;
- node=node1;
- while(node!=null){
- System.out.print(node.getKey()+"\t");
- k++;
- node=node.getRight();
- }
- System.out.print("\t");
- System.out.print("("+k+")");
- //System.out.print(node.getKey());
- System.out.println();
- //node=node1.getDown();
- node1=node1.getDown();
- }
- }
- }
- class Node{
- public String key;
- public int value;
- public Node up, down,left , right;
- public static String tou=new String("--头--");
- public static String wei=new String("--尾--");
- public Node(String k, int v){
- this.key=k;
- this.value=v;
- up=down=left=right=null;
- }
- public void setUp(Node up){
- this.up=up;
- }
- public Node getUp(){
- return up;
- }
- public void setDown(Node down){
- this.down=down;
- }
- public Node getDown(){
- return down;
- }
- public void setLeft(Node left){
- this.left=left;
- }
- public Node getLeft(){
- return left;
- }
- public void setRight(Node right){
- this.right=right;
- }
- public Node getRight(){
- return right;
- }
- public void setKey(String k){
- this.key=k;
- }
- public String getKey(){
- return key;
- }
- public void setValue(int v){
- this.value=v;
- }
- public int getValue(){
- return value;
- }
- }
- package 跳跃表;
- public class Test {
- public static void main(String[] args){
- SkipList s = new SkipList();
- // s.add("AAA", 122);
- int i=0;
- for(;i<30;i++){ //随机数字进行测试
- s.add(String.valueOf(i), i);
- }
- s.print();
- System.out.println("\n\n----------\n\n\n");
- if(s.find("22")!=null){ //查找
- System.out.println("\nOK");
- }else{//找不到
- System.out.println("\nfalse");
- }
- s.delNode("0"); //删除
- s.print();
- }
- }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· C#/.NET/.NET Core优秀项目和框架2025年2月简报
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 【杭电多校比赛记录】2025“钉耙编程”中国大学生算法设计春季联赛(1)