平衡樹真的噁心死了!!!!!!好煩啊,又臭又長。
有很多種平衡樹,替罪羊, treap,fhq, slpay。這裡就說 splay, 和 bst 和 替罪羊 了,因為其他我都不會(悲
先說二叉排序樹(二叉搜尋樹), 他的關係就是 左子樹所有節點 < 根節點 < 右子樹所有節點。也就是說,按照中序遍歷可以找到有序序列。
這個時候我們就可以增刪改(刪了再加入)查了!
查詢 透過搜尋,發現如果這個節點等於自己,cnt++, 小於自己往左搜尋,大於自己往右邊搜尋
比如說我們要增加,搜尋,如果發現沒有,就新增節點。
刪除, 找到位置(同上)cnt--。
這個時候就發現一個很嚴重的問題,它的複雜度是樹高,就有可能變成一條鏈,複雜度瞬間變成 \(O(n)\) (泰褲辣)。
平衡樹要來了!
平衡樹還有幾個功能
- 要求排名
- 按照排名查數
- 前驅後繼
替罪羊的思路就此誕生。 如果發現不平衡,就重構。
平衡的定義是非0節點佔全部的 M% 以內,且左節點的個數在 右節點的 M% 一內。
發現不平衡後就把它變成線性的,再按照貪心思路,每次折半重構。
M 在 70 ~ 75 為最佳
程式碼就不貼了,因為很醜。
接下來就是 splay 了
splay的主要思路就是操作的數都要旋轉到樹頂端
這個時候就要提到左旋和右旋了。其實就是傳統的左旋和右旋,還是講一下吧。
如圖是左旋,右旋就是反過來,將x往上面旋,只需要看x是左兒子,還是右兒子就可以了。
然而,無腦的旋轉會被善良的出題人卡掉。
所以,我們就要牽扯到祖宗——爸爸的爸爸叫什麼(
兩種情況,第一種,如下圖
先旋轉自己,再旋轉自己。
第二種,如下圖
先轉父親,再轉自己。
然後就是緊張刺激的寫程式碼環節了!
bool check(int x) {
return a[a[x].fa].ch[0] != x;
}
寫一個函式判斷是做兒子(0)還是右兒子(1)。
void push_up(int rt) {
a[rt].size = a[a[rt].ch[0]].size + a[a[rt].ch[1]].size + a[rt].cnt;
}
更新自己,記得加上自己的個數。
void Add(int x, int y, bool f) {
a[y].ch[f] = x;
a[x].fa = y;
}
x接在y的(f ? "右兒子" : "左兒子")上。
正片,開始。
如果x是左兒子,就左旋,否則就是右旋,這樣就可以簡單輕鬆的完成自動的完成旋轉了(dig, zig害人啊)
void rotate(int x) {
int y = a[x].fa, z = a[y].fa, d = check(x), w = a[x].ch[d ^ 1];
Add(w, y, d);
Add(x, z, check(y));
Add(y, x, d ^ 1);
push_up(y), push_up(x);
}
單次旋轉函式,將x向上旋轉。
void splay(int x, int p = 0) {
for (int f = a[x].fa; f = a[x].fa, f != p; rotate(x)) {
if (a[f].fa != p) {
if (check(f) == check(x)) {
rotate(f);
} else {
rotate(x);
}
}
}
if (p == 0) Rt = x;
}
多次旋轉函式,將 x 旋頂。
void find(int x) {
int p = Rt;
while (a[p].ch[x > a[p].v] && x != a[p].v) {
p = a[p].ch[x > a[p].v];
}
splay(p);
}
查詢並旋轉到頂。
void insert(int x) {
int p = Rt, fa = 0;
while (p && x != a[p].v) {
fa = p;
p = a[p].ch[x > a[p].v];
}
if (p)
a[p].cnt++;
else {
p = ++cnt;
if (fa) a[fa].ch[x > a[fa].v] = p;
a[p] = MakeSplay(x, fa);
}
splay(p);
}
找到,如果已經有了就cnt++,否則新增。
int pre_suc(int x, int f) {
find(x);
if (!f && a[Rt].v < x || f && a[Rt].v > x) return Rt;
int p = a[Rt].ch[f];
while (a[p].ch[f ^ 1]) {
p = a[p].ch[f ^ 1];
}
return p;
}
前驅0, 後驅1。先旋轉到頂,然後查詢。
注意了,刪除不一樣
void remove(int x) {
int last = pre_suc(x, 0), next = pre_suc(x, 1);
splay(last), splay(next, last);
int p = a[next].ch[0];
if (a[p].cnt > 1) {
a[p].cnt--;
splay(p);
} else {
a[next].ch[0] = 0;
push_up(next), push_up(last);
}
}
先把x的前驅旋轉到頂,再將後繼旋到前驅的後面。那麼後繼的左兒子就是 x,而且 x 沒有兒子。
int rank1(int x) {
int p = Rt;
while (1) {
if (a[p].ch[0] && a[a[p].ch[0]].size >= x) {
p = a[p].ch[0];
} else if (x > a[a[p].ch[0]].size + a[p].cnt) {
x -= a[a[p].ch[0]].size + a[p].cnt;
p = a[p].ch[1];
} else {
return p;
}
}
}
查排名為x的數,不多解釋。
總程式碼來咯。
等等等等,還要加入最大值和最小值哦,不然有可能會沒有前驅和後繼哦(親身
#include <iostream>
using namespace std;
const int kMaxN = 1e5 + 5;
int Rt, cnt;
struct Splay {
int v, fa, cnt, size, ch[2];
} a[kMaxN];
Splay MakeSplay(int v, int fa) {
Splay P;
P.v = v, P.fa = fa, P.cnt = P.size = 1, P.ch[0] = P.ch[1] = 0;
return P;
}
bool check(int x) {
return a[a[x].fa].ch[0] != x;
}
void push_up(int rt) {
a[rt].size = a[a[rt].ch[0]].size + a[a[rt].ch[1]].size + a[rt].cnt;
}
void Add(int x, int y, bool f) {
a[y].ch[f] = x;
a[x].fa = y;
}
void rotate(int x) {
int y = a[x].fa, z = a[y].fa, d = check(x), w = a[x].ch[d ^ 1];
Add(w, y, d);
Add(x, z, check(y));
Add(y, x, d ^ 1);
push_up(y), push_up(x);
}
void splay(int x, int p = 0) {
for (int f = a[x].fa; f = a[x].fa, f != p; rotate(x)) {
if (a[f].fa != p) {
if (check(f) == check(x)) {
rotate(f);
} else {
rotate(x);
}
}
}
if (p == 0) Rt = x;
}
void find(int x) {
int p = Rt;
while (a[p].ch[x > a[p].v] && x != a[p].v) {
p = a[p].ch[x > a[p].v];
}
splay(p);
}
void insert(int x) {
int p = Rt, fa = 0;
while (p && x != a[p].v) {
fa = p;
p = a[p].ch[x > a[p].v];
}
if (p)
a[p].cnt++;
else {
p = ++cnt;
if (fa) a[fa].ch[x > a[fa].v] = p;
a[p] = MakeSplay(x, fa);
}
splay(p);
}
int pre_suc(int x, int f) {
find(x);
if (!f && a[Rt].v < x || f && a[Rt].v > x) return Rt;
int p = a[Rt].ch[f];
while (a[p].ch[f ^ 1]) {
p = a[p].ch[f ^ 1];
}
return p;
}
void remove(int x) {
int last = pre_suc(x, 0), next = pre_suc(x, 1);
splay(last), splay(next, last);
int p = a[next].ch[0];
if (a[p].cnt > 1) {
a[p].cnt--;
splay(p);
} else {
a[next].ch[0] = 0;
push_up(next), push_up(last);
}
}
int rank1(int x) {
int p = Rt;
while (1) {
if (a[p].ch[0] && a[a[p].ch[0]].size >= x) {
p = a[p].ch[0];
} else if (x > a[a[p].ch[0]].size + a[p].cnt) {
x -= a[a[p].ch[0]].size + a[p].cnt;
p = a[p].ch[1];
} else {
return p;
}
}
}
int main() {
insert(0x3f3f3f3f);
insert(-0x3f3f3f3f);
int n, op, x;
for (cin >> n; n; n--) {
cin >> op >> x;
switch (op) {
case 1:
insert(x);
break;
case 2:
remove(x);
break;
case 3:
find(x), cout << a[a[Rt].ch[0]].size << '\n';
break;
case 4:
cout << a[rank1(x + 1)].v << '\n';
break;
case 5:
cout << a[pre_suc(x, 0)].v << '\n';
break;
case 6:
cout << a[pre_suc(x, 1)].v << '\n';
break;
}
}
return 0;
}