【算法】03_跳表的原理以及应用

1 跳表基础

1.1 概念

跳表是特殊的链表,其特点如下:

  • 多层组成,每一层都是有序链表
  • 一个元素在某一层出现,一定会出现在其下一层的链表中
  • 最底层是原始链表,包含跳表中所有元素
  • 链表中的节点维护了指向其他节点的指针
  • 目的是快速访问/插入/更新/删除
    在这里插入图片描述

1.2 复杂度分析

跳表性质:

  • 第k级索引的节点个数是k-1级索引节点个数的一半,第k级索引的节点个数是n/(2^k)
  • 第k级索引的节点个数是k-1级索引节点个数的一半,原始链表节点个数是n,最高级别的索引节点数是2,n/(2^h)=2,其索引层数是log(n)-1
  • 第k级索引的节点个数是k-1级索引节点个数的一半,原始链表节点个数是n,其索引总节点数是 n/2, n/4, n/8, ..., 8, 4, 2,等比数列求和公式n-2,或者是n-2^(-n-1))

1.2.1 时间复杂度分析

  • 查询一个数据的时间复杂度是log(n)
    链表总节点数是n,其高度是log(n)-1 + 1 = log(n),每一层遍历的节点数最大是3,因此,查找到目标数字的时间复杂度是3*log(n)=log(n)。跳表是基于单链表和二分查找的原理,借助二分思想实现快速查找。

1.2.2 空间复杂度分析

  • 空间复杂度是n
    第k级索引的节点个数是k-1级索引节点个数的一半,索引节点的个数是n-2

2 跳表应用

2.1 Redis有序集合

redis中的有序集合是集成了哈希表以及跳表。

  • 哈希表查找单个元素,时间复杂度O(1)
  • 跳表方便区间查找

2.2 平衡数据结构对比

No.跳表平衡查找树
1概率性平衡(随机确定是否添加下一级索引节点)严格平衡
2原理简单,编码容易实现原理较复杂,有已实现案例如HashMap
2查找 时间复杂度:平均log(n),最坏n查找 时间复杂度:平均log(n)

3 跳表模版

class Skiplist {
        /**
         * https://leetcode-cn.com/problems/design-skiplist/solution/java-jing-jian-shi-xian-by-stg/
         */

        Node head = new Node(0, null, null);
        Random random = new Random();
        Node[] stack = new Node[64];


        public Skiplist() {

        }

        /**
         * 从左向右,从上到下
         *
         * @param target
         * @return
         */
        public boolean search(int target) {
            // 指针下沉到下一层
            for (Node p = head; p != null; p = p.down) {
                while (p.right != null && p.right.val < target) {
                    // 索引上的节点值小雨目标值,需要指针右移
                    p = p.right;
                }
                // p是本层索引最后一个节点,或者其右侧节点值大于目标值
                if (p.right != null && p.right.val == target) {
                    return true;
                }
            }
            return false;
        }

        /**
         * 1 找到目标位置的前一个节点
         * 2
         *
         * @param num
         */
        public void add(int num) {
            int lv = -1;
            for (Node p = head; p != null; p = p.down) {
                while (p.right != null && p.right.val < num) {
                    p = p.right;
                }
                stack[++lv] = p;
            }
            boolean isInsertUp = true;
            Node downNode = null;
            while (isInsertUp && lv >= 0) {
                Node insert = stack[lv--];
                insert.right = new Node(num, insert.right, downNode);
                downNode = insert.right;
                // 是否在在上一层插入此节点的索引
                isInsertUp = (random.nextInt() & 1) == 0;
            }
            if (isInsertUp) {
                head = new Node(0, new Node(num, null, downNode), head);
            }
        }

        public boolean erase(int num) {
            boolean isExists = false;
            for (Node p = head; p != null; p = p.down) {
                while (p.right != null && p.right.val < num) {
                    p = p.right;
                }
                /**
                 * 在每一层,找到目标节点的前一个节点
                 * 如果删除的是索引未知的节点,for循环还会继续下沉到愿链表进行删除
                 *
                 */
                if (p.right != null && p.right.val <= num) {
                    isExists = true;
                    p.right = p.right.right;
                }
            }
            return isExists;
        }

        class Node {
            int val;
            Node right;
            Node down;


            Node(int x) {
                val = x;
            }

            Node(int x, Node right, Node down) {
                val = x;
                this.right = right;
                this.down = down;
            }
        }
    }

引用

https://juejin.im/post/57fa935b0e3dd90057c50fbc

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值