美文网首页
跳跃表原理及java实现跳跃表

跳跃表原理及java实现跳跃表

作者: 田才 | 来源:发表于2020-02-10 21:11 被阅读0次

    对于一个链表的查询和列表不同。如果列表是有序的那么可以用二分查找,时间复杂度为log n,链表即便是有序的,查询一个元素也是要从头遍历,时间复杂度为O(n) 。为了解决链表的查询效率问题,出现了一些特殊的数据结构,例如:树类的包括1、二叉树 2、平衡二叉树(理想最好情况)3、红黑树 4、b 树 5、b+ 树 等等,还有一种区别于树类型的数据结构:跳跃表。跳跃表类似多维链表,最高纬跳跃最大,最低纬就是普通的链表存储了所有的数据。例如链表 7 - 21 - 32 - 37 -71 - 85 如下图


    image.png

    1、初始化先由下到上创建一个head竖向链表,Node的值为空,无穷小。

    2、跳跃表的查找过程,例如查找的是节点32。红线是指针变化过程,虚线是判断过程。如图

    image.png

    3、插入:首先查找到需要插入节点的最低纬链表的位置指针,并且记录每个维度最左位置的指针。然后随机生成高度。例如插入的数值为35,随机高度为4,如图


    image.png

    4、性能
    查找、插入和删除操作的时间复杂度都为: O(logn)

    随机跳跃表表现性能也非常不错,节省了大量复杂的调节平衡树的代码。其效率与红黑树、伸展树等这些平衡树能够说相差不大。
    但跳跃表还在并发环境下有优势。在并发环境下。假设要更新数据,跳跃表须要更新的部分就比較少,锁的东西也就比較少。所以不同线程争锁的代价就相对少了,而红黑树有个平衡的过程,牵涉到大量的节点,争锁的代价也就相对较高了。跳跃表的缺点是,存储空间占用相对较大一些。

    5、应用
    jdk 中有对跳跃表的并发实现:
    ConcurrentSkipListMap
    ConcurrentSkipListSet

    了解过Redis的都知道,Redis有一个非常实用的数据结构:SortSet,基于它,我们能够非常轻松的实现一个Top N的应用。这个SortSet底层就是利用跳表实现的。

    总结
    作为一种简单的数据结构,在大多数应用中Skip lists可以取代平衡树。Skiplists算法很easy实现、扩展和改动。
    Skip lists和进行过优化的平衡树有着相同高的性能 (红黑树)。

    java代码实现:

    import java.util.ArrayList;
    import java.util.List;
    import java.util.concurrent.ThreadLocalRandom;
    
    public class SkipList<E, C extends Comparable<? super C>> {
        //最大层级
        static final int MAX_LEVEL = 1 << 6;
        static final int DEFAULT_LEVEL = 4;
        //跳跃表 down node 的层级
        int level;
        //头节点
        Node<E, C> head;
        ThreadLocalRandom random = ThreadLocalRandom.current();
    
        public SkipList() {
            this(DEFAULT_LEVEL);
        }
    
        //跳跃表的初始化
        public SkipList(int level) {
            this.level = level;
            int i = level;
            Node<E, C> temp = null;
            Node<E, C> prev = null;
            //从底部节点开始创建链表
            while (i-- != 0) {
                temp = new Node(null, (T) -> -1);
                temp.down = prev;
                prev = temp;
            }
            head = temp;//头节点
        }
    
    
        public void put(E val, C score) {
            Node<E, C> t = head;
            //存储每个层级,插入新节点的最左边节点
            List<Node<E, C>> paths = new ArrayList<>();
            //1,找到需要插入的位置
            while (t != null) {
                //表示存在该值的点,表示需要更新该节点
                if (t.score.compareTo(score) == 0) {
                    Entity e = t.entity;
                    //插入到链表尾部
                    e.next = new Entity(val, null);
                    return;
                }
    
                //查找插入的位置
                if (t.next == null || t.next.score.compareTo(score) > 0) {
                    paths.add(t);
                    t = t.down;
                } else {
                    t = t.next;
                }
            }
            //随机生成新节点的层级
            int lev = randomLevel();
    
            // 如果随机的层级数,大于head节点的层级数
            // 需要更新head这一列的节点数量,保证head列数量最多
            if (lev > level) {
                Node<E, C> temp = null;
                Node<E, C> prev = head;
                while (level++ != lev) {
                    temp = new Node(null, (T) -> -1);
                    paths.add(0, temp);
                    temp.down = prev;
                    prev = temp;
                }
                head = temp;
                //更新层级
                level = lev;
            }
            //从后向前遍历path中的每一个节点,在其后面增加一个新的节点
            Node<E, C> down = null, newNade = null, path = null;
            Entity<E> e = new Entity<>(val, null);
            for (int i = level - 1; i >= level - lev; i--) {
                //创建新的节点
                newNade = new Node<E, C>(e, score);
                path = paths.get(i);
                newNade.next = path.next;
                path.next = newNade;
                newNade.down = down;
                down = newNade;
            }
        }
    
        /**
         * 查找跳跃表中的一个值
         */
        public List<E> get(C score) {
            Node<E, C> t = head;
            while (t != null) {
                if (t.score.compareTo(score) == 0) {
                    return t.entity.toList();
                }
                if (t.next == null || t.next.score.compareTo(score) > 0) {
                    t = t.down;
                } else {
                    t = t.next;
                }
            }
            return null;
        }
    
        /**
         * 查找跳跃表中指定范围的值
         */
        public List<E> find(final C begin, C and) {
            Node<E, C> t = head;
            Node<E, C> beginNode = null;
            //确定下界索引
            while (t != null) {
                beginNode = t;
                if (t.next == null || t.next.score.compareTo(begin) >= 0) {
                    t = t.down;
                } else {
                    t = t.next;
                }
            }
    
            //循环链表
            List<E> list = new ArrayList<>();
            for (Node n = beginNode.next; n != null; n = n.next) {
                if (n.entity != null && n.score.compareTo(and) < 0)
                    list.addAll(n.entity.toList());
            }
            return list;
        }
    
        /**
         * 根据score的值来删除节点。
         */
        public void delete(C score) {
            //1,查找到节点列的第一个节点的前驱
            Node<E, C> t = head;
            while (t != null) {
                if (t.next == null || t.next.score.compareTo(score) > 0) {
                    //向下查找
                    t = t.down;
                } else if (t.next.score.compareTo(score) == 0) {
                    // 在这里说明找到了该删除的节点
                    t.next = t.next.next;
                    //向下查找
                    t = t.down;
                } else {
                    //向右查找
                    t = t.next;
                }
            }
        }
    
        /**
         * 高度随机增加一
         */
        private int randomLevel() {
            int lev = 1;
            //产生一个非负整数
            while (random.nextInt() % 2 == 0)
                lev++;
            return Math.min(lev, MAX_LEVEL);
        }
    
        /**
         * 跳跃表的节点链表
         */
        static class Node<E, C extends Comparable> {
            Entity<E> entity;//存储的数据
            C score; //需要排序的字段
            Node<E, C> next;//指向下一个节点的指针
            Node<E, C> down;//指向下一层的指针
    
            Node(Entity<E> entity, C score) {
                this.entity = entity;
                this.score = score;
            }
    
            @Override
            public String toString() {
                return "Node{score=" + score + '}';
            }
        }
    
        static class Entity<E> {
            E val;
            Entity<E> next;
    
            Entity(E val, Entity<E> next) {
                this.val = val;
                this.next = next;
            }
    
            List<E> toList() {
                List<E> list = new ArrayList<>();
                list.add(val);
                for (Entity<E> e = next; e != null; e = e.next) {
                    list.add(e.val);
                }
                return list;
            }
    
            @Override
            public String toString() {
                return "Entity{" + "val=" + val + '}';
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:跳跃表原理及java实现跳跃表

          本文链接:https://www.haomeiwen.com/subject/cnkzxhtx.html