口胡平衡樹splay

一.什麼是平衡樹

就這樣給你說,平衡樹就是一個節點的左兒子嚴格小於它,右兒子嚴格大於它的一棵二叉樹,整個樹的葉子結點的深度幾乎一致。

二.分步講解

1.定義變量

key[i]:排序的關鍵字
cnt[i]:每個節點的數的個數
f[i]:父節點
ch[i][0]:左兒子;ch[i][1]:右兒子
siz[i]:以i爲根的子樹大小
sz:節點總數
root:根節點的編號

4.預備函數

void clear (int x){//清零
    ch[x][0] = ch[x][1] = key[x] = cnt[x] = f[x] = siz[x] = 0;
}

void update (int x){//更新
    siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
}

3.旋轉操作

給一張圖就行了
在這裏插入圖片描述
看懂了嗎?

void rotate (int x){
    int fa = f[x], gfa = f[fa], d = (x == ch[fa][1]);//d是找x是fa的哪一個兒子節點
    ch[fa][d] = ch[x][1 ^ d];
    f[ch[fa][d]] = fa;//***
    ch[x][1 ^ d] = fa;
    f[fa] = x;
    f[x] = gfa;
    if (gfa){
        int d1 = (fa == ch[gfa][1]);
        ch[gfa][d1] = x;
    }
    update (x), update (fa), update (gfa);//注意更新
}

3.splay

splay的作用就是把當前節點旋轉到根,那麼根就更新了,並且樹是平衡的。

void splay (int x){
    for (int fa; fa = f[x]; rotate(x))
        if (f[fa])
            rotate ((ch[f[fa]][1] == fa) == (ch[fa][1] == x) ? fa : x);
            //這裏要注意,如果當前節點,父親節點和爺爺節點在一條線上,就要先旋轉父親節點,再旋轉當前節點,注意不能失衡
    root = x;
}

4.插入

void insert (int x){
    if (! root){//第一種情況,是空樹
        sz ++;
        ch[sz][0] = ch[sz][1] = f[sz] = 0;
        cnt[sz] = siz[sz] = 1;
        key[sz] = x;
        root = sz;
        return ;
    }
    int now = root, fa = 0;
    while (1){//第二種情況,要插到葉節點去
        if (key[now] == x){
            cnt[now] ++;
            update (now);
            update (fa);
            splay (now);//注意要旋轉到根部
            return ;
        }
        fa = now;
        now = ch[now][x > key[now]];
        if (! now){
            sz ++;
            ch[sz][0] = ch[sz][1] = 0;
            f[sz] = fa;
            key[sz] = x;
            cnt[sz] = siz[sz] = 1;
            ch[fa][x > key[fa]] = sz;
            update (fa);
            splay (sz);
            return ;
        }
    }
}

5.查詢x的排名

int find (int x){
    int ans = 0, now = root;
    while (1){
        if (key[now] > x){//如果當前節點關鍵字比x大,則查找左兒子
            now = ch[now][0];
        }
        else{
            ans += ch[now][0] ? siz[ch[now][0]] : 0;
            if (x == key[now]){//找到了
                splay (now);
                return ans + 1;
            }
            ans += cnt[now];
            now = ch[now][1];//以此類推
        }
    }
}

6.查詢排名x的數

int findx (int x){//很簡單,容易理解
    int now = root;
    while (1){
        if (ch[now][0] && siz[ch[now][0]] >= x)
            now = ch[now][0];
        else{
            x -= ch[now][0] ? siz[ch[now][0]] : 0;
            if (x <= cnt[now])
                return key[now];
            x -= cnt[now];
            now = ch[now][1];
        }
    }
}

7.前驅(比x小的最大的)

int pre (){
    int now = ch[root][0];
    while (ch[now][1])
        now = ch[now][1];
    return now;
}

8.後繼(比x大的最小的)

int nex (){
    int now = ch[root][1];
    while (ch[now][0])
        now = ch[now][0];
    return now;
}

9.刪除

void del (int x){//一共有四種情況,詳見下
    find (x);//先把當前節點旋到根
    if (cnt[root] > 1){//如果當前節點的個數不止一個,就直接減
        cnt[root] --;
        return ;
    }
    if (! ch[root][0] && ! ch[root][1]){//如果當前節點沒有子節點,就直接刪
        clear (root);
        root = 0;
        return ;
    }
    //如果只有一個子節點,就讓其子節點成爲根
    if (! ch[root][0]){
        int oldroot = root;
        root = ch[root][1];
        f[root] = 0;
        clear (oldroot);
        return ;
    }
    else if (! ch[root][1]){
        int oldroot = root;
        root = ch[root][0];
        f[root] = 0;
        clear (oldroot);
        return ;
    }
    //如果有兩個子節點,就把左面最大的後輩旋到根,然後更新節點信息就行了
    int oldroot = root, furoot = pre ();
    splay (furoot);
    ch[root][1] = ch[oldroot][1];
    f[ch[oldroot][1]] = root;
    update (root);
    clear (oldroot);
}

三.模板

此題是模板題:【模板】普通平衡樹
Code:

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;

#define M 100005

int n, ch[M][2], key[M], cnt[M], f[M], siz[M], root, sz;

void clear (int x){
    ch[x][0] = ch[x][1] = key[x] = cnt[x] = f[x] = siz[x] = 0;
}
void update (int x){
    siz[x] = siz[ch[x][0]] + siz[ch[x][1]] + cnt[x];
}
void rotate (int x){
    int fa = f[x], gfa = f[fa], d = (x == ch[fa][1]);
    ch[fa][d] = ch[x][1 ^ d];
    f[ch[fa][d]] = fa;//***
    ch[x][1 ^ d] = fa;
    f[fa] = x;
    f[x] = gfa;
    if (gfa){
        int d1 = (fa == ch[gfa][1]);
        ch[gfa][d1] = x;
    }
    update (x), update (fa), update (gfa);
}
void splay (int x){
    for (int fa; fa = f[x]; rotate(x))
        if (f[fa])
            rotate ((ch[f[fa]][1] == fa) == (ch[fa][1] == x) ? fa : x);
    root = x;
}
void insert (int x){
    if (! root){
        sz ++;
        ch[sz][0] = ch[sz][1] = f[sz] = 0;
        cnt[sz] = siz[sz] = 1;
        key[sz] = x;
        root = sz;
        return ;
    }
    int now = root, fa = 0;
    while (1){
        if (key[now] == x){
            cnt[now] ++;
            update (now);
            update (fa);
            splay (now);
            return ;
        }
        fa = now;
        now = ch[now][x > key[now]];
        if (! now){
            sz ++;
            ch[sz][0] = ch[sz][1] = 0;
            f[sz] = fa;
            key[sz] = x;
            cnt[sz] = siz[sz] = 1;
            ch[fa][x > key[fa]] = sz;
            update (fa);
            splay (sz);
            return ;
        }
    }
}
int find (int x){
    int ans = 0, now = root;
    while (1){
        if (key[now] > x){
            now = ch[now][0];
        }
        else{
            ans += ch[now][0] ? siz[ch[now][0]] : 0;
            if (x == key[now]){
                splay (now);
                return ans + 1;
            }
            ans += cnt[now];
            now = ch[now][1];
        }
    }
}
int findx (int x){
    int now = root;
    while (1){
        if (ch[now][0] && siz[ch[now][0]] >= x)
            now = ch[now][0];
        else{
            x -= ch[now][0] ? siz[ch[now][0]] : 0;
            if (x <= cnt[now])
                return key[now];
            x -= cnt[now];
            now = ch[now][1];
        }
    }
}
int pre (){
    int now = ch[root][0];
    while (ch[now][1])
        now = ch[now][1];
    return now;
}
int nex (){
    int now = ch[root][1];
    while (ch[now][0])
        now = ch[now][0];
    return now;
}
void del (int x){
    find (x);
    if (cnt[root] > 1){//***
        cnt[root] --;
        return ;
    }
    if (! ch[root][0] && ! ch[root][1]){
        clear (root);
        root = 0;
        return ;
    }
    if (! ch[root][0]){
        int oldroot = root;
        root = ch[root][1];
        f[root] = 0;
        clear (oldroot);
        return ;
    }
    else if (! ch[root][1]){
        int oldroot = root;
        root = ch[root][0];
        f[root] = 0;
        clear (oldroot);
        return ;
    }
    int oldroot = root, furoot = pre ();
    splay (furoot);
    ch[root][1] = ch[oldroot][1];
    f[ch[oldroot][1]] = root;
    update (root);
    clear (oldroot);
}
int main (){
    scanf ("%d", &n);
    while (n --){
        int opt, x;
        scanf ("%d %d", &opt, &x);
        if (opt == 1)
            insert (x);
        if (opt == 2)
            del (x);
        if (opt == 3)
            printf ("%d\n", find (x));
        if (opt == 4)
            printf ("%d\n", findx (x));
        if (opt == 5){
            insert (x);
            printf ("%d\n", key[pre ()]);
            del (x);
        }
        if (opt == 6){
            insert (x);
            printf ("%d\n", key[nex ()]);
            del (x);
        }
    }
    return 0;
}

謝謝!

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