设计跳表(leetcode 1206)

跳表是平衡树的一种替代方案,优点是实现较为简单。它以多于原始数据一倍的存储空间换取查询效率。
发明的动机应该是想对链表进行二分查找,逐渐演化出的算法。
跳表在工程领域有广泛的应用。 比如 redis的zset,rocksdb中的内存部分实现等。也是一些语言基础包中的数据结构。

跳表,下面这篇文章写的真的很不错

跳表解释的好文章

于是读完之后仿照思路实现了一遍,可以拿 leetcode 1206 来验证正确性.

实现过程中体会到下面几点

1.  跳表采用空间换时间的方式,比原始数据存储量增加一倍左右,概率上每一层是上一层的数据量的一半。原题目数据量  <= 50000, 本题目按照一般情况设置为16层。
2.  不管是插入还是删除一个元素,最关键是找到么一层中符合条件插入位置或者删除元素的上一个位置。是一条分界线, 核心下面的代码中searchHit(int num)函数。  
3.  在吗每一层头部添加哨兵,尾部共同聚合到一个值为无穷大的节点,可以简化代码的编写
4.  跳表的节点采用预分配的形式,并采用垃圾回收机制. 

leetcode 1206     的代码

class Skiplist {
public:

    // 预分配和垃圾回收机制
    const static int MAX_LEVEL = 16, INF = 30010, N = 50010;
    float ratio = 0.5;
     struct Node {
        int down = 0, next = 0, val , maxLevel = 0;
        void init(int _val, int _maxLevel, int _down, int _next){
            down =_down, val = _val, maxLevel =_maxLevel, next = _next;
        }
    };
    int idx = 0, head, nodes[N], tt = -1; 
    Node tr[N];

    Skiplist() {

       // 初始化尾部哨兵节点  
       tr[0].init(INF, 0, 0, 0);
       int u = head = getNode();
       tr[u].init(-INF, MAX_LEVEL, 0, 0);
       // 为每一层添加头部哨兵节点
       for (int i = MAX_LEVEL - 1; i > 0; i--) {
          int v = getNode();
          tr[u].down = v;
          tr[v].init(-INF, i, 0, 0);
          u = v;
       }
    }

    int getNode() {
        if (tt < 0) nodes[++tt] = ++ idx;
        int u = nodes[tt--];
        return u;
    }

    void gcNode(int u) {
        nodes[++tt] = u;
    }
    
    // 核心函数
    int update[MAX_LEVEL + 1];
    int searchHit(int num) {
        int u  = head, hit = 0, p;
        for (int level = MAX_LEVEL; level > 0; level --) {
           p = u;
           while (tr[p].val < num) u = p, p = tr[p].next;
           if (tr[p].val == num) hit = max(level, hit);
           update[level] = u;
           u = tr[u].down;
        }
        return hit;
    } 

    bool search(int target) {
       return searchHit(target);
    }
    
    void add(int num) {

        searchHit(num);
        int last = 0, randLevel = randomLevel(), t, c, u;
        for (int level = randLevel; level > 0; level --) {
            u = getNode();
            Node & node = tr[u];
            node.init(num, level, 0, 0);
            c = update[level];
            t = tr[c].next;
            tr[c].next = u, node.next = t;
            if (last) tr[last].down = u;
            last = u;
        }
    }
    
    bool erase(int num) {
        
        int hit = searchHit(num);
        if (!hit) return false;

        int p, t;
        for(int level = hit; level > 0; level --) {
            p = update[level];
            t = tr[p].next;// t 一定存在
            tr[p].next = tr[t].next;
            gcNode(t);
        } 
        return true;
    }
    /**
     * P(level=1)=0.5 P(level=2)=0.25 ...
     */
    int randomLevel() {
       int level = 0;
       while (rand() % 100 < ratio * 100 && level < MAX_LEVEL) {
           level ++;
       }
       return level + 1;
    }
};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值