bst平衡树

来源:互联网 发布:mac 命令行下载文件 编辑:程序博客网 时间:2024/06/07 20:12

之前syh大神讲的时候完全不知道什么东西
在ljt和lrj的帮助下终于弄得懂了
bst是排序二叉树
简单来说就是中序遍历的结果是由小到大排序的结果
一般建树过程从根节点开始如果没有点就直接变成新的节点
如果有点就比较大小
大数去右子树
小数去左子树
重复上述操作

一般的我们认为如果数据是足够随机的话
那么bst的深度大约为logn
但是一些奇怪的数据比如直接123456.。。。。。
会使bst变成一条链
树的深度会变得很扭曲

这时我们不妨人为的让数据随机
这就是treap
我们给每个点定义一个prior
这个值用rand来取
在这treap上
我们可以通过一些旋转使prior按堆排列
这样bst就会长得像堆一样,深度非常的平衡

然后目的明确了
我们想想操作
首先对于bst我们可以进行旋转
旋转的本质就是让自己的某个儿子成为自己的父亲(很奇怪的味道)
我们思考如果这个儿子成为了父亲,那么原来的父亲的这边的子树就没了
让儿子成为父亲,就会让儿子和父亲相连,原来儿子的一个子树也无法连接。
那么正好这个多出来的子树就连在空缺处
很好理解吧

对于建treap
我们先插入节点上面提到过,
之后每插入一个节点就对于这个点和他的父子节点比较prior
如果不符合堆的性质,
就旋转。

清除
把节点往下旋转至没有两棵子树时,
直接清除节点,让子树连接父亲。(如果无子树就忽略)

通常我们可以给bst加一个size表示子树的规模
由此结合bst的性质快速进行一些操作
例如第k大数和rank

一道模板题
codevs1164

#include<cstdio>#include<iostream>#include<cstring>#include<algorithm>#include<map> #include<ctime>using namespace std;template  <typename T>class node{    public:    T data;    node *left_child,*right_child;    int prior;};template <typename T>class treap{    private:    node<T> *root;    void change_right(node<T>* &n )    {        node<T>*temp=n->left_child;        n->left_child=temp->right_child;        temp->right_child=n;        n=temp;        temp=0;    }    void change_left(node<T>* &n){        node<T>*temp=n->right_child;        n->right_child=temp->left_child;        temp->left_child=n;        n=temp;        temp=0;    }    void put(node<T>* &n,T data){        if(!n){            n=new node<T>;            n->data=data;            n->left_child=n->right_child=0;            n->prior=rand()%1000000007;            return ;        }        if(data == n->data){            n->data=data;        }        else if(data<n->data){            put (n->left_child,data);            if(n->left_child->prior<n->prior)                change_right(n);        }        else{            put (n->right_child,data);            if(n->right_child->prior<n->prior){                change_left(n);            }        }    }    node<T> *get(node<T>* n,T data){        if(!n)            return 0;        if(n->data==data)            return n;        else if(data<n->data)            return get(n->left_child,data);        else             return get(n->right_child,data);    }    void del(node<T> * n)    {        if(n){            del(n->left_child);            del(n->right_child);            delete n;        }    }    void erase_T(node<T>* &tree,T n){        if(!tree)            return ;        if(!(tree->data==n)){            if(n<tree->data)                erase_T(tree->left_child);            else erase-T(tree->right_child);            }           if(!tree->left_child&&!tree->right_child)        {            delete tree;            tree=0;        }        else if(tree->left_child&&tree->right_child){            if(tree->left_child->prior<=tree->right_child->prior){                change_right(tree);                erase_T(tree->right_child,n);            }            else{                change_left(tree);                erase_T(tree->left_child,n);            }        }else if(tree->left_child){            change_right(tree);            erase_T(tree->right_child,n);        }           else if(tree->right_child){            change_left(tree);            erase_T(tree->left_child,n);        }    }    void dfs_lr(node<T>* &n,void(*function)(T t)){        if(!n) return ;        dfs_lr(n->left_child,function);        function(n->data);        dfs_lr(n->right_child,function);    }    public :    treap()    {        root=0;        srand(time(NULL));      }       ~treap()    {        del(root);    }    void insert(T data){        put(root,data);    }    node<T> *find(T data){        return get(root,data);    }    bool empty()    {        return (root==0);    }    void clear(){        del (root);    }    bool count(T data){        return find(data)!=0;    }    void erase(T data){        erase_T(root,data);    }    void dfs(void(*function)(T t)){        dfs_lr(root,function);    }};struct p{    int x;    int times;    friend bool operator<(p a,p b){        return a.x<b.x;     }    friend bool operator ==(p a,p b){        return a.x==b.x;    }};void out(p a){    printf("%d %d\n",a.x,a.times);}int main(){    treap<p> tr;    int n,temp;    scanf("%d",&n);    for(int i=1;i<=n;i++){        scanf("%d",&temp);        p a;        a.x=temp;        if(!tr.find(a)){            a.times=1;            tr.insert(a);        }        else {            a.times=tr.find(a)->data.times+1;            tr.insert(a);        }    }    tr.dfs(out);    return 0;}

接下来看看splay
splay和treap相似
但是没有随机的prior
但是会有独特的伸展操作
对于伸展树可以合并和分裂区间

我们开始研究非旋treap
首先他叫treap所以他依然符合treap的性质每个点有一个prior
prior依然符合堆得性质,而每个点的val依然符合一个bst的性质
其次我们来看看非旋
很多的平衡树都是需要旋转的,然后不旋转的树通常可以用于可持久化
然后主要是用merge和split两种操作来实现。
然后merge操作就是合并两颗treap a,b
那么操作很简单如果a< b swap(a,b)
然后a的右子树=merge(a的右子树,b)
然后交换一下a的左右子树来满足插入期望,看不懂这个原因记住要交换就行;

资料
seg

阅读全文
1 0