leetcode【困难】1206、设计跳表

跳表 是在 O(log(n)) 时间内完成增加、删除、搜索操作的数据结构。
在这里插入图片描述

在这里插入图片描述
第一层节点个数为 n,位于第 i 层的节点有 p 的概率出现在第 i+1 层,p为常数。假设共有 m 层
空间复杂度为O(n)
在这里插入图片描述
跳表的查询,插入和删除操作的期望时间复杂度都为O(logn)
在这里插入图片描述
为了计算查找长度,我们将逆向还原查找过程,从右下方第一层最后到达的那个节点开始,沿着查找路径向左,向上回溯。假设当回溯到某个节点的时候,它才被插入。

  • 如果节点x有第i+1,那么我们需要向上走,这种情况概率为p
  • 如果节点没有第i+1层指针,那么我们需要向左走,这种情况的概率为1-p
    用C(k)表示向上攀爬k个层级所需要走过的平均查找路径长度,因此有

在这里插入图片描述

思路:
查找过程中,每比较一次要么向下一层,要么到本层的右侧节点。每个节点在进行插入的时候,它的层数是由随机函数randomLevel()计算出来的,随机的计算不依赖于其它的节点,每次插入过程都是完全独立的。
位于第 i 层的节点有 p 的概率出现在第 i+1 层

redis中,p = 1/4,MaxLevel = 32

class Node{
    Integer value; //节点值
    Node[] next; // 节点在不同层的下一个节点
	// 用size表示当前节点在跳表中索引几层
	// 比如7在第四层的,那第四层下面共有4个7
    public Node(Integer value,int size) { 
        this.value = value;
        this.next = new Node[size];
    }
}
  1. 先随机出来一个层数,new要插入的节点,先叫做插入节点newNode;
  2. 根据跳表实际的总层数从上往下分析,要插入一个节点newNode时,先找到节点在该层的位置:因为是链表,所以需要一个节点node,满足插入插入节点newNode的值刚好不大于node的下一个节点值,当然,如果node的下个节点为空,那么也是满足的。
  3. 确定插入节点newNode在该层的位置后,先判断下newNode的随机层数是否小于当前跳表的总层数,如果是,则用链表的插入方法将newNode插入即可。
  4. 如此循环,直到最底层插入newNode完毕。
  5. 循环完毕后,还需要判断下newNode随机出来的层数是否比跳表的实际层数还要大,如果是,直接将超过实际层数的跳表的头节点指向newNode即可,该跳表的实际层数也就变为newNode的随机层数了。
class Skiplist {
    //最大层数        
    private static int DEFAULT_MAX_LEVEL = 32;
    //随机层数概率,也就是随机出的层数,在第1层以上(不包括第一层)的概率,
    //层数不超过maxLevel,层数的起始号为1
    private static double DEFAULT_P_FACTOR = 0.25;

    Node head = new Node(null,DEFAULT_MAX_LEVEL); //头节点

    int currentLevel = 1; //表示当前nodes的实际层数,它从1开始


    public Skiplist() {}

    public boolean search(int target) {
        Node searchNode = head;
        for (int i = currentLevel-1; i >=0; i--) {
            searchNode = findClosest(searchNode, i, target);
            if (searchNode.next[i]!=null && searchNode.next[i].value == target){
                return true;
            }
        }
        return false;
    }


    public void add(int num) {
        int level = randomLevel();
        Node updateNode = head;
        Node newNode = new Node(num,level);
        // 计算出当前num 索引的实际层数,从该层开始添加索引
        for (int i = currentLevel-1; i>=0; i--) {
            //找到本层最近离num最近的list
            updateNode = findClosest(updateNode,i,num);
            if (i<level){
                if (updateNode.next[i]==null){
                    updateNode.next[i] = newNode;
                }else{
                    Node temp = updateNode.next[i];
                    updateNode.next[i] = newNode;
                    newNode.next[i] = temp;
                }
            }
        }
        if (level > currentLevel){ //如果随机出来的层数比当前的层数还大,那么超过currentLevel的head 直接指向newNode
            for (int i = currentLevel; i < level; i++) {
                head.next[i] = newNode;
            }
            currentLevel = level;
        }

    }

    public boolean erase(int num) {
        boolean flag = false;
        Node searchNode = head;
        for (int i = currentLevel-1; i >=0; i--) {
            searchNode = findClosest(searchNode, i, num);
            if (searchNode.next[i]!=null && searchNode.next[i].value == num){
                //找到该层中该节点
                searchNode.next[i] = searchNode.next[i].next[i];
                flag = true;
                continue;
            }
        }
        return flag;
    }

    //找到level层, value 大于node 的节点
    private Node findClosest(Node node,int levelIndex,int value){
        while ((node.next[levelIndex])!=null && value >node.next[levelIndex].value){
            node = node.next[levelIndex];
        }
        return node;
    }


    //随机一个层数
    private static int randomLevel(){
        int level = 1;
        while (Math.random()<DEFAULT_P_FACTOR && level<DEFAULT_MAX_LEVEL){
            level ++ ;
        }
        return level;
    }


    class Node{
        Integer value;//节点值
        Node[] next;// 节点在不同层的下一个节点

        public Node(Integer value,int size) {// size表示当前节点在跳表中索引几层
            this.value = value;
            this.next = new Node[size];
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值