Treap学习笔记

初步认识 Intro

Treap(树堆)是一种 弱平衡 的 二叉搜索树。它同时符合二叉搜索树和堆的性质,名字也因此为 tree(树)和 heap(堆)的组合。 ————OIWiki

\(Treap\)中每个节点设置两个值:\(val\)\(pri\),分别是值和优先级,在\(Treap\)中,\(pri\)是随机的。在树中,\(val\)是符合二叉搜索树而\(pri\)符合堆性质,这种平衡方式使其能在\(O(logn)\)的时间内完成操作。

\(Treap\)的实现主要依靠旋转,分左旋和右旋两种,他们都可以将根节点向下旋转并且不破坏它的性质。

旋转操作 Zig-Zag

\(Treap\)中的两种基本操作为左旋和右旋,如图所示,左旋将根节点旋转到了左边,将右儿子转到了根部
image
代码实现如下

 inline void zig(Node *&cur){ //左旋:把右儿子旋为父亲
	Node *tmp = cur->rchild;
	cur->rchild = tmp->lchild;
	tmp->lchild = cur;
	cur->updt();	//更新节点数据
	tmp->updt();
	cur = tmp;
}
inline void zag(Node *&cur){ //右旋:把左儿子旋为父亲
	Node *tmp = cur->lchild;
    cur->lchild = tmp->rchild;
    tmp->rchild = cur;
    cur->updt();
    tmp->updt();
    cur = tmp;
}

插入 Insert

插入操作基于左旋右旋操作,主要思想为将点插入叶子节点处,再通过旋转将节点移到树中合适的位置。

值得注意的是每个节点会有一个\(cnt\),若在树中找到了一样的点,增加副本数\(cnt\)即可

代码实现:

inline void __insert(Node*&cur, int val){
        if(cur == nullptr){
            //不存在此节点
            //创建新的节点
            cur = create(val);
            return;
        }
        if(cur->val == val){
            //以前就有这个节点
            //加多副本数量即可
            cur->cnt++;
            cur->size++;
            return;
        }
        //BST
        if(cur->val > val){
            __insert(cur->lchild, val);
            //经过插入 要检查优先级是否有变化
            if(cur->rchild != nullptr && cur->rchild->pri < cur->pri)
                zag(cur);
        }
        else{
            __insert(cur->rchild, val);
            if(cur->lchild != nullptr && cur->lchild->pri < cur->pri)
                zig(cur);
        }
        cur->updt();
        return;
    }

删除 Delete

删除操作和插入类似,需先找到节点,然后将其旋转到可以直接删除的位置。

在遍历到的节点会有四种情况:

  1. 当前节点的值不等于想要删除的点,需要继续找
  2. 当前节点的值为要删掉的点,且它有大于1点副本数,此时直接改变\(cnt\)\(size\)即可
  3. 当前节点的值为要删掉的点,且只有一个,此时需要将儿子节点中\(pri\)小的选择上来,将当前节点旋转下去
  4. 当前节点的值为要删除的点,且只有一个或没有儿子,此时可以直接删除
    对于四种情况分别讨论即可

代码实现如下:

inline void __del(Node*&cur, int val){
    if(cur->val != val){
        //BST
        if(cur->val > val)
            __del(cur->lchild, val);
        else
            __del(cur->rchild, val);
        cur->updt();
        return;
    }
    if(cur->cnt > 1){
        cur->cnt--;
        cur->size--;
        return;
    }
    if(cur->lchild == nullptr || cur->rchild == nullptr){
        if(cur->lchild == nullptr && cur->rchild == nullptr)
            delete cur; //叶子节点可直接删除
        //只有一个孩子节点的可以直接删去
        //将孩子节点直接接上即可
        else if(cur->lchild == nullptr){
            Node *tmp = cur;
            cur = tmp->rchild;
            delete tmp;
        }
        else{
            Node *tmp = cur;
            cur = tmp->lchild;
            delete tmp;
        }
        return;
    }
    //将优先级低的转上去作为根
    //此时要删除的节点就被转到这个点的另一侧
    //于是递归继续删除
    //直到转到可以直接删除的位置
    if(cur->lchild->pri < cur->rchild->pri){
        zag(cur);
        __del(cur->rchild, val);
    }
    else{
        zig(cur);
        __del(cur->lchild, val);
    }
    cur->updt();
    return;
}

完整代码:

#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;

typedef struct Node{
    Node *lchild;
    Node *rchild;
    int val;
    int pri;
    int cnt;
    int size;
    inline void updt(){
        size = cnt;
        if(lchild != nullptr)
            size += lchild->size;
        if(rchild != nullptr)
            size += rchild->size;
        return;
    }
}Node;
inline Node* create(int val){
    Node *newNode = new Node;
    newNode->val = val;
    newNode->cnt = newNode->size = 1;
    newNode->pri = rand();
    return newNode;
}
class Treap{
private:
    Node *root;
    inline void zig(Node*&cur){  //左旋:把右儿子旋为父亲
        Node *tmp = cur->rchild;
        cur->rchild = tmp->lchild;
        tmp->lchild = cur;
        cur->updt();
        tmp->updt();
        cur = tmp;
    }
    inline void zag(Node*&cur){  //右旋:把左儿子旋为父亲
        Node *tmp = cur->lchild;
        cur->lchild = tmp->rchild;
        tmp->rchild = cur;
        cur->updt();
        tmp->updt();
        cur = tmp;
    }
    inline void __insert(Node*&cur, int val){
        if(cur == nullptr){
            //不存在此节点
            //创建新的节点
            cur = create(val);
            return;
        }
        if(cur->val == val){
            //以前就有这个节点
            //加多副本数量即可
            cur->cnt++;
            cur->size++;
            return;
        }
        //BST
        if(cur->val > val){
            __insert(cur->lchild, val);
            //经过插入 要检查优先级是否有变化
            if(cur->rchild != nullptr && cur->rchild->pri < cur->pri)
                zag(cur);
        }
        else{
            __insert(cur->rchild, val);
            if(cur->lchild != nullptr && cur->lchild->pri < cur->pri)
                zig(cur);
        }
        cur->updt();
        return;
    }
    inline void __del(Node*&cur, int val){
        if(cur->val != val){
            //BST
            if(cur->val > val)
                __del(cur->lchild, val);
            else
                __del(cur->rchild, val);
            cur->updt();
            return;
        }
        if(cur->cnt > 1){
            cur->cnt--;
            cur->size--;
            return;
        }
        if(cur->lchild == nullptr || cur->rchild == nullptr){
            if(cur->lchild == nullptr && cur->rchild == nullptr)
                delete cur; //叶子节点可直接删除
            //只有一个孩子节点的可以直接删去
            //将孩子节点直接接上即可
            else if(cur->lchild == nullptr){
                Node *tmp = cur;
                cur = tmp->rchild;
                delete tmp;
            }
            else{
                Node *tmp = cur;
                cur = tmp->lchild;
                delete tmp;
            }
            return;
        }
        //将优先级低的转上去作为根
        //此时要删除的节点就被转到这个点的另一侧
        //于是递归继续删除
        //直到转到可以直接删除的位置
        if(cur->lchild->pri < cur->rchild->pri){
            zag(cur);
            __del(cur->rchild, val);
        }
        else{
            zig(cur);
            __del(cur->lchild, val);
        }
        cur->updt();
        return;
    }
    inline void __traverse(Node *cur){  //遍历序列,检查
        if(cur == nullptr)
            return;
        __traverse(cur->lchild);
        for(int i = 1; i <= cur->cnt; i++)
            cout << cur->val << " ";
        __traverse(cur->rchild);
        return;
    }
public:
    inline int size(){
        return root->size;
    }
    inline void insert(int val){
        __insert(root, val);
    }
    inline void del(int val){
        __del(root, val);
    }
    inline void traverse(){
        __traverse(root);
        cout << endl;
    }
};
Treap T;
int main(){
    int q;
    cin >> q;
    while(q--){
        int op, x;
        cin >> op >> x;
        if(op == 1)
            T.insert(x);
        else if(op == 2)
            T.del(x);
        cout << T.size() << endl;
        T.traverse();
    }
}
posted @ 2024-05-28 11:25  HurryCine  阅读(132)  评论(0)    收藏  举报