跳表的原理及实现——有序表3

目录

一、跳表的原理

二、跳表的插入和删除

三、跳表的代码实现


一、跳表的原理

        对于单链表,即使是 存储的有序数据(即 有序链表),想在其中查找某个数据,也只能从头到尾遍历,查找效率低,时间复杂度是O(n),如下图所示:

        为了提高查找效率,使用二分查找的思想,对有序链表建立一级“索引”。 每两个节点提取一个节点到索引层。 索引层中的每个节点 都包含两个指针,一个指向下一个节点,一个down指针,指向下一级节点。

假设要查找图中 18这个节点:

        首先在一级索引层遍历,当遍历到14这个节点的时候,发现其下一个节点是23,则要查找的18就在14和23之间。 然后,通过14节点的 down 指针,下降到原始链表这一层,继续在原始链表中遍历。 此时,只需要在原始链表中,遍历两个节点,14和18,就找到18这个节点了。    查找18这个节点,在原始链表需要遍历10个节点,现在只需要遍历7个节点(一级索引层遍历5个节点,原始链表遍历2个节点)。 

从以上示例可以看出,加上一层索引之后,查找一个节点的遍历节点数减少了,效率提高了。如果再增加一级索引,那么效率会不会更高呢?

建立二级索引

        与建立一级索引的方式类似,在第一级索引的基础上,每两个节点抽出一个节点到第二级索引,如下图:

         现在如果要查找18节点,只需要遍历6个节点(二级索引层遍历3个节点,一级索引层1个节点,原始链表2个节点)。

        通过建立索引的方式,对于数据量越大的有序链表,通过建立多级索引,查找效率提升会非常明显。

        这种链表加多级索引的结构 就是 跳表。

二、跳表的插入和删除

        跳表作为一个动态数据结构,不仅支持查找操作,还支持数据的插入和删除操作,并且 插入和删除的操作的时间复杂度都是O(logn).

        插入操作
为了保证原始链表中数据的有序性,我们需要先找到新数据应该插入的位置。 可以基于多级索引,快速查找到新数据的插入位置,时间复杂度为O(log n)。

        假设插入数据为6的节点,如下图:

        删除操作
删除原链表中的节点,如果节点存在于索引中,也要删除索引中的节点。 因为单链表中的删除需要用到 要删除节点 的 前驱动节点。 可以像插入操作一样,通过索引逐层向下遍历到原始链表中,要删除的节点,并记录其 前驱节点,从而实现删除操作。

三、跳表的代码实现

        本代码是学习左神跳表视频时所写,供大家一起学习。

public class SkipListMapClass {

    //跳表的节点定义
    public static class SkipListNode<K extends Comparable<K>, V>{
        public K key;
        public V val;
        public ArrayList<SkipListNode<K,V>> nextNodes;

        public SkipListNode(K k,V v){
            key = k;
            val = v;
            nextNodes = new ArrayList<SkipListNode<K,V>>();
        }

//        遍历的时候,如果是往右遍历到的null(next == null),遍历结束
//        头(null),头节点的null认为是最小的
//        node ->头,node(null," ")  node.isKeyLess(!null) true

//        node 里面的key是否比otherKey小,true,不是false
        public boolean isKeyLess(K otherKey){
            return otherKey != null && (key == null || key.compareTo(otherKey) < 0);
        }

        public boolean isKeyEqual(K otherKey){
            return (key == null && otherKey == null) ||
                    (key != null && otherKey != null && key.compareTo(otherKey) == 0);
        }
    }

    public static class SkipListMap<K extends Comparable<K>,V>{
        private static final double PROBABILITY = 0.5;
        private SkipListNode<K,V> head;
        private int size;
        private int maxLevel;

        public SkipListMap(){
            head = new SkipListNode<K,V>(null,null);
            head.nextNodes.add(null);   //最底层节点为0层节点
            size = 0;
            maxLevel = 0;
        }

        //从最高层开始,一路找下去
        //最终,找到第0层的 < key 的最右的节点
        private SkipListNode<K,V> mostRightLessNodeInTree(K key){
            if (key == null){
                return null;
            }
            int level = maxLevel;
            SkipListNode<K,V> cur = head;
            while (level >= 0){//从上层跳到下层
                //cur level -> level-1
                cur = mostRightLessNodeInLevel(key,cur,level--);
            }
            return cur;
        }

        //在level层里,往右移动
        //现在来到的节点是cur,来到cur的level层,在level层上,找到 <key 最后一个节点并返回
        private SkipListNode<K,V> mostRightLessNodeInLevel(K key,SkipListNode<K,V> cur,int level){
            SkipListNode<K,V> next = cur.nextNodes.get(level);
            while (next != null && next.isKeyLess(key)) {
                cur = next;
                next = cur.nextNodes.get(level);
            }
            return cur;
        }

        public boolean containKey(K key){
            if (key == null){
                return false;
            }
            SkipListNode<K,V> less = mostRightLessNodeInTree(key);
            SkipListNode<K,V> next = less.nextNodes.get(0);
            return next != null && next.isKeyEqual(key);
        }

        public void put(K key,V value){
            if (key == null){
                return;
            }

            //0层上,最右一个, < key 的Node
            SkipListNode<K,V> less = mostRightLessNodeInTree(key);
            SkipListNode<K,V> find = less.nextNodes.get(0);
            if (find != null &&find.isKeyEqual(key)){//修改
                find.val = value;
            }else {//新增
                size++;
                int newNodeLevel = 0;
                while(Math.random() < PROBABILITY){
                    newNodeLevel++;
                }
                while (newNodeLevel > maxLevel){
                    head.nextNodes.add(null);
                    maxLevel++;
                }
                SkipListNode<K,V> newNode = new SkipListNode<K,V>(key,value);
                for (int i =0;i <=newNodeLevel;i++){
                    newNode.nextNodes.add(null);
                }
                int level = maxLevel;
                SkipListNode<K,V> pre = head;
                while (level >= 0){
                    pre = mostRightLessNodeInLevel(key,pre,level);
                    if (level <= newNodeLevel){
                        newNode.nextNodes.set(level,pre.nextNodes.get(level));
                        pre.nextNodes.set(level,newNode);
                    }
                    level--;
                }
            }
        }

        public V get(K key){
            if (key == null){
                return null;
            }
            SkipListNode<K,V> less = mostRightLessNodeInTree(key);
            SkipListNode<K,V> next = less.nextNodes.get(0);
            return next!=null && next.isKeyEqual(key)?next.val:null;
        }

        public void remove(K key){
            if (containKey(key)){
                size--;
                int level = maxLevel;
                SkipListNode<K,V> pre = head;
                while (level >= 0){
                    pre = mostRightLessNodeInLevel(key,pre,level);
                    SkipListNode<K,V> next = pre.nextNodes.get(level);

                    if (next !=null && next.isKeyEqual(key)){
                        pre.nextNodes.set(level,next.nextNodes.get(level));
                    }
                    if (level!=0&& pre == head && pre.nextNodes.get(level)==null){
                        head.nextNodes.remove(level);
                        maxLevel--;
                    }
                    level--;
                }
            }
        }

        public K firstKey(){
            return head.nextNodes.get(0) != null ? head.nextNodes.get(0).key : null;
        }

        public K lastKey(){
            int level = maxLevel;
            SkipListNode<K,V> cur = head;
            while (level >= 0){
                SkipListNode<K,V> next = cur.nextNodes.get(level);
                while (next != null){
                    cur = next;
                    next = cur.nextNodes.get(level);
                }
                level--;
            }
            return cur.key;
        }

        public K ceillingKey(K key){
            if (key == null){
                return null;
            }
            SkipListNode<K,V> less = mostRightLessNodeInTree(key);
            SkipListNode<K,V> next = less.nextNodes.get(0);
            return next != null ? next.key:null;
        }

        public K floorKey(K key){
            if (key == null){
                return null;
            }
            SkipListNode<K,V> less = mostRightLessNodeInTree(key);
            SkipListNode<K,V> next = less.nextNodes.get(0);
            return next!= null && next.isKeyEqual(key)?next.key:less.key;
        }

        public int size(){
            return size;
        }

    }


    public static void printAll(SkipListMap<String,String> obj){
        for (int i = obj.maxLevel;i>=0;i--){
            System.out.print("Level "+i+" : ");
            SkipListNode<String,String> cur = obj.head;
            while (cur.nextNodes.get(i) != null){
                SkipListNode<String,String> next = cur.nextNodes.get(i);
                System.out.print("("+next.key+" , "+next.val+")");
                cur = next;
            }
            System.out.println();
        }
    }

    public static void main(String[] args) {
        SkipListMap<String,String> test = new SkipListMap<>();
        printAll(test);
        System.out.println("================");
        test.put("A","10");
        printAll(test);
        System.out.println("================");
        test.remove("A");
        printAll(test);
        System.out.println("================");
        test.put("E","E");
        test.put("B","B");
        test.put("A","A");
        test.put("F","F");
        test.put("C","C");
        test.put("D","D");
        printAll(test);
        System.out.println("================");
        System.out.println(test.containKey("B"));
        System.out.println(test.containKey("Z"));
        System.out.println(test.firstKey());
        System.out.println(test.lastKey());
        System.out.println(test.floorKey("D"));
        System.out.println(test.ceillingKey("D"));
        System.out.println("================");
        test.remove("D");
        printAll(test);
        System.out.println("================");
        System.out.println(test.floorKey("D"));
        System.out.println(test.ceillingKey("D"));

    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值