Splay Tree源碼

注:此代碼非本人所寫,實現方式爲自底向上,在做ACM題目時比教科書上的自頂向下在數據處理上要好操作。

由於個人現在比較鍾愛指針,所以從網上找了個指針實現的源碼,只是添加了點個人的理解註釋和一些其他操作。

http://dongxicheng.org/structure/splay-tree/此博客對Splay Treee分析比較透徹,需要注意的是,Splay Tree的旋轉操作與其他平衡樹如Red Black Tree的操作在代碼上有些不同,具體可以畫圖得出。

理解了Splay Tree的Splay函數和其對區間操作的方法,基本上就掌握Splay Tree了。

#include <iostream>
#include <cstdio>
#include <cstdlib>
using namespace std;
int const maxn=300000;

#define bint __int64
struct node
{
    int value,size;
    int add;
    bint sum;
    node *ch[2], *pre;
}tree[maxn],*Null, *root;//設置一個Null指針是一個亮點

int num[maxn];
int n,m;

int top=0;
node *New_Node(int x)
{
    node *p=&tree[top++];
    p->ch[0]=p->ch[1]=p->pre=Null;
    p->add=0;
    p->sum=x;
    p->size=1;
    p->value=x;
    return p;
}

void Push_Down(node *x)
{
    if (x == Null) return;
    x->value+=x->add;
    x->ch[0]->add+=x->add;
    x->ch[1]->add+=x->add;
    if(x->ch[0]!=Null)
        x->ch[0]->sum+=(bint)(x->add)*(x->ch[0]->size);
    if(x->ch[1]!=Null)
        x->ch[1]->sum+=(bint)(x->add)*(x->ch[1]->size);
    x->add=0;
}

void Update(node *x)
{
    if (x == Null) return;
    x->size = x->ch[0]->size + x->ch[1]->size + 1;
    x->sum = (bint)x->value+ x->ch[0]->sum + x->ch[1]->sum;
}

//這裏的旋轉和redblack不同,是將節點x向上旋
void Rotate(node *x, int c)//c=0: left rotate c=1: right rotate
{
    node *y = x->pre;
    Push_Down(y);
    Push_Down(x);
    y->ch[! c] = x->ch[c];
    x->pre = y->pre;
    if (x->ch[c] != Null)
        x->ch[c]->pre = y;
    if (y->pre != Null)
        y->pre->ch[y->pre->ch[1] == y] = x;//判斷原來y->pre的左或右孩子是y
    y->pre = x;
    x->ch[c] = y;
    if (y == root) root = x;
    Update(y);
}

//將節點x放到節點f的下面
void Splay(node *x, node *f)
{
    Push_Down(x);
    while(x->pre != f)
    {
        if (x->pre->pre == f)
            Rotate(x, x->pre->ch[0] == x);
        else
        {
            node *y = x->pre, *z = y->pre;
            if (z->ch[0] == y)//left
                if (y->ch[0] == x) //LL
                    Rotate(y, 1), Rotate(x, 1);
                else
                    Rotate(x, 0), Rotate(x, 1);//LR
            else//right
                if (y->ch[1] == x) //RR
                    Rotate(y, 0), Rotate(x, 0);
                else
                    Rotate(x, 1), Rotate(x, 0);//RL
        }
    }
    Update(x);
}

//找到處在中序遍歷第K個節點,並將其旋轉到節點f的下面
void Select(int k, node *f)
{
    node *now=root;
    while(true)
    {
        Push_Down(now);
        int tmp = now->ch[0]->size;
        if (tmp + 1 == k) break;
        if (k <= tmp)
            now = now->ch[0];
        else
            now = now->ch[1], k -= tmp + 1;
    }
    //printf("Select: %d\n",now->value);
    Splay(now, f);
}

node *Make_Tree(int l, int r, node *fa)
{
    if (l > r) return Null;
    int mid = (l + r) >> 1;
    node *p = New_Node(num[mid]);
    p->ch[0] = Make_Tree(l, mid-1, p);
    p->ch[1] = Make_Tree(mid+1, r, p);
    p->pre = fa;
    Update(p);
    return p;
}

void remove(int left,int right){//刪除區間[left,right]
    Select(left,Null);
    Select(right+2,root);
    root->ch[1]->ch[0] = Null;
    Splay(root->ch[1],Null);
}

void ADD(int left,int right,int cnt){
    Select(left,Null);
    Select(right+2,root);
    root->ch[1]->ch[0]->add += cnt;
    root->ch[1]->ch[0]->sum+=(bint)cnt*(root->ch[1]->ch[0]->size);
    Splay(root->ch[1]->ch[0],Null);
}

node *makeTree(int l, int r, int value, node *fa)
{
    if (l > r) return Null;
    int mid = (l + r) >> 1;
    node *p = New_Node(value);
    p->ch[0] = makeTree(l, mid-1,value, p);
    p->ch[1] = makeTree(mid+1, r,value, p);
    p->pre = fa;
    Update(p);
    return p;
}

void insert(int pos,int cnt,int value){//在pos位置後面連續插入cnt個值爲value的數
    Select(pos+1,Null);
    Select(pos+2,root);
    root->ch[1]->ch[0] = makeTree(1,cnt,value,root->ch[1]);
    Splay(root->ch[1]->ch[0],Null);
}

void print(node *root){
    node *p = root;
    if(p!=Null){
        if(p->ch[0]!=Null)print(p->ch[0]);
        printf("%d,parent=%d,size=%d\n",p->value,p->pre->value,p->pre->size);
        if(p->ch[1]!=Null) print(p->ch[1]);
    }
}

int main()
{
    top=0;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&num[i]);
    }

    Null=New_Node(0);
    Null->size=0;

    //head
    root=New_Node(-1);
    //tail
    root->ch[1]=New_Node(-1);
    root->ch[1]->pre=root;
    Update(root);

    //root->ch[1] is the really root
    root->ch[1]->ch[0]=Make_Tree(1,n,root->ch[1]);
    Update(root->ch[1]);
    Update(root);
    //print(root);
    char s[2];
    for(int i=0;i<m;i++)
    {

        scanf("%s",s);
        if(s[0]=='C')
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            ADD(a,b,c);
          //  Select(a,Null);
           // print(root);
         //   Select(b+2,root);
           // cout<<"=================================="<<endl;
           // print(root);
            //root->ch[1]->ch[0]->add+=c;
            //root->ch[1]->ch[0]->sum+=(bint)c*(root->ch[1]->ch[0]->size);
        }
        else
        {
            int a,b;
            scanf("%d%d",&a,&b);
            //先把第a-1的數節點放到Null下,再將第b+1的數節點放到root下
            //(即root的右子樹),那麼剩下(root左子樹就是區間[a,b])
            Select(a,Null);//由於root的size是1,所以這裏是a-1+1=a,a-1就變成root了
            //print(root);
            Select(b+2,root);//同樣這裏是b+1+1=b+2,a-1的右子樹
           // cout<<"=================================="<<endl;
           // print(root);
            printf("%I64d\n",root->ch[1]->ch[0]->sum);
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章