力扣 1206. 设计跳表 跳表

https://leetcode-cn.com/problems/design-skiplist/
力扣 1206. 设计跳表 跳表
思路:原理参见

class Listnode
{
public:
    //val-值 count-出现的次数
    int val,count;
    //forwards[i]代表在第i级(level)的情况下 该节点的下一节点 1代表最低级 相当于普通链表\
    //这里还有一个隐含信息:该节点是第forwards.size() 级的节点
    vector<Listnode*> forwards;
    Listnode(int v=-1,int level=0):val(v),count(1),forwards(level,nullptr){}
    int get_level(){ return forwards.size(); }
};

class Skiplist {
public:
    //限制跳表的最大level
    const int max_level=16;
    //目前最高level
    int cur_level;
    //跳表头节点
    Listnode *head;

    Skiplist():head(new Listnode(-1,max_level)) {}

    ~Skiplist() { delete head; }

    //得到一个level 
    int random_level()
    {
        int level=1;
        while((rand()&1)&&level<max_level)
            ++level;
        return level;
    }

    //查找指定节点 
    Listnode* _search(int target)
    {
        //当前节点 下一节点
        Listnode *cur=head,*nxt;
        for(int i=cur_level-1;i>=0;i--)
        {
            while((nxt=cur->forwards[i])&&nxt->val<target)
                cur=nxt;
            //找到 返回其前驱
            if(nxt&&nxt->val==target)
                return nxt;
        }
        return nullptr;
    }

    //查找指定节点的前驱 为了便于获得后继 需要level记录层级
    Listnode* _search_before(int target,int &level)
    {
        //当前节点 下一节点
        Listnode *cur=head,*nxt;
        for(int i=cur_level-1;i>=0;i--)
        {
            while((nxt=cur->forwards[i])&&nxt->val<target)
                cur=nxt;
            //找到
            if(nxt&&nxt->val==target)
            {
                level=i;
                return cur;
            }
        }
        return nullptr;
    }
    
    //查找target是否存在
    bool search(int target) {
        return _search(target)!=nullptr;
    }
    
    //添加num到跳表中
    void add(int num) {
        //查找指定节点
        Listnode *cur=_search(num);
        //若存在 直接递增计数即可
        if(cur)
            ++cur->count;
        //否则需要在跳表中新增节点
        else
        {
            //为新加节点随机一个level
            int level=random_level();
            if(cur_level<level)
                cur_level=level;
            //新节点
            Listnode *newnode=new Listnode(num,level);
            cur=head;
            Listnode *nxt;
            //注意此处循环要从cur_level开始 而不要从level开始
            //因为level可能远小于cur_level 如果此时num是一个较大的数 会浪费很多时间
            for(int i=cur_level-1;i>=0;i--)
            {
                while((nxt=cur->forwards[i])&&nxt->val<num)
                    cur=nxt;
                //在第i+1级新增节点 注意i+1要<=level 即i<level
                if(i<level)
                {
                    newnode->forwards[i]=nxt;
                    cur->forwards[i]=newnode;
                }
            }
        }
    }
    
    //从跳表中删除num
    bool erase(int num) {
        int level;
        //待删节点前驱
        Listnode *cur=_search_before(num,level);
        //若存在
        if(cur)
        {
            Listnode *del=cur->forwards[level];
            level=del->get_level();
            //需要递减其count 若递减后count=0 需要从跳表中移除
            if(--del->count==0)
            {
                Listnode *nxt;
                for(int i=level-1;i>=0;i--)
                {
                    while((nxt=cur->forwards[i])&&nxt!=del)
                        cur=nxt;
                    if(nxt)
                        cur->forwards[i]=nxt->forwards[i];
                }
                //回收空间
                delete del;
            }
            return true;
        }
        return false;
    }
};

/**
 * Your Skiplist object will be instantiated and called as such:
 * Skiplist* obj = new Skiplist();
 * bool param_1 = obj->search(target);
 * obj->add(num);
 * bool param_3 = obj->erase(num);
 */
上一篇:跳表——怎么让一个有序链表能够进行“二分“查找?


下一篇:简单的个人介绍网页-开场特效2(HTML+CSS)【附代码】