剛看了一天的平衡樹,我就過來小結了......
只過了幾道板子題,,,
FHQ-treap的基本原理
基本原理就是用分裂(split)和合並(merge)的方式完成平衡樹的操作
兩種基本操作及其原理
好像我也不太懂 ...wtcl
split 分裂 的基本原理
Split(rt,c,&x,&y) 將以rt節點爲根的子樹按 參數 c 分裂爲兩顆子樹 x, y,並返回其編號。
以按權值分裂爲例,稱權值較小的樹爲左樹,另一棵爲右樹。
根據二叉搜索樹左小右大的性質,可確定分裂的過程:
比較根節點權值 與 給定權值的大小關係
若小於給定權值,則根及左子樹點都小於等於給定權值,將其歸入左樹中。向右子樹遞歸,繼續分裂子樹。
否則,根及右子樹點都大於等於給定權值,將其歸入右樹中。向左子樹遞歸,繼續分裂子樹。
遞歸到葉子節點退出.
split code
void split(int rt, int c, int &x, int &y) {
if (!rt) {
x = y = 0;
return;
}
if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
else y = rt, split(tree[rt].l, c, x, tree[rt].l);
push_up(rt);
}
merge 合併的基本思路
merge(x,y) 將以x,y節點爲根的兩棵樹合併爲一棵,並返回新樹根的編號。
特別注意:合併的兩棵樹 一定是 Split 分裂獲得的兩棵樹
而且只要merge出現,前邊一定有一個split, 而且合併的子樹一定是split分裂的樹
merge code
int merge(int x, int y) {
if (!x || !y) return x + y;
if (tree[x].rand < tree[y].rand) {
tree[x].r = merge(tree[x].r, y), push_up(x);
return x;
} else {
tree[y].l = merge(x, tree[y].l), push_up(y);
return y;
}
}
基於分離和合並的平衡樹操作
插入一個數
我們先把樹分爲x,y兩部分,然後把新的節點a看做是一棵樹,先與x合併,合併完之後將合併的整體與y合併
void insert(int c) {
split(root, c, tmp1, tmp2);
root = merge(merge(tmp1, newnode(c)), tmp2);
}
刪除一個數
首先我們把樹分爲x和z兩部分
那麼x樹中的最大權值爲a
再把x分爲x和y兩部分。
此時x中的最大權值爲a-1,且權值爲a的節點一定是y的根節點。
然後我們可以無視y的根節點,直接把y的左右孩子合併起來,這樣就成功的刪除了根節點,
最後再把x,y,z合併起來就好
void shan(int x) {
split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
root = merge(merge(tmp1, tmp2), tmp3);
}
查詢a的排名
我們首先按照a-1的權值把樹分開。
那麼x樹中最大的應該是a-1。
那麼a的排名就是siz[x]+1
void query_rank(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[tmp1].size + 1);
root = merge(tmp1, tmp2);
}
查詢排名爲a的數
直接調用查找排名的函數即可,
這個函數應該比較好理解。
printf("%d\n", tree[kth(root, x)].val);
求x的前驅(前驅定義爲小於a,且最大的數)
因爲要小於a,那麼我們按照a-1的權值劃分,
x中最大的一定是<=a-1的,
所以我們直接輸出x中最大的數就好,
(這裏有一個小技巧,因爲siz儲存的是節點的數目,然後根據二叉查找樹的性質,編號最大的就是值最大的)
void query_pre(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
root = merge(tmp1, tmp2);
}
求x的後繼(後繼定義爲大於x,且最小的數)
和上一個原理類似
void query_suc(int x) {
split(root, x, tmp1, tmp2);
printf("%d\n", tree[kth(tmp2, 1)].val);
root = merge(tmp1, tmp2);
}
code
#include <bits/stdc++.h>
#define ll long long
#define N 100010
#define M 1010
using namespace std;
int n, tmp1, tmp2, tmp3, add_node, root;
struct node {
int l, r, size, val, rand;
}tree[N];
int read() {
int s = 0, f = 0; char ch = getchar();
while (!isdigit(ch)) f |= (ch == '-'), ch = getchar();
while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar();
return f ? -s : s;
}
void push_up(int rt) {
tree[rt].size = tree[tree[rt].l].size + tree[tree[rt].r].size + 1;
}
int newnode(int c) {
add_node++;
tree[add_node].val = c, tree[add_node].size = 1;
tree[add_node].rand = rand();
return add_node;
}
void split(int rt, int c, int &x, int &y) {
if (!rt) {
x = y = 0;
return;
}
if (tree[rt].val <= c) x = rt, split(tree[rt].r, c, tree[rt].r, y);
else y = rt, split(tree[rt].l, c, x, tree[rt].l);
push_up(rt);
}
int merge(int x, int y) {
if (!x || !y) return x + y;
if (tree[x].rand < tree[y].rand) {
tree[x].r = merge(tree[x].r, y), push_up(x);
return x;
} else {
tree[y].l = merge(x, tree[y].l), push_up(y);
return y;
}
}
int kth(int now, int key) {
while (1) {
if (key <= tree[tree[now].l].size) {
now = tree[now].l;
continue;
}
if (key == tree[tree[now].l].size + 1) return now;
key -= tree[tree[now].l].size + 1;
now = tree[now].r;
}
}
void insert(int c) {
split(root, c, tmp1, tmp2);
root = merge(merge(tmp1, newnode(c)), tmp2);
}
void shan(int x) {
split(root, x, tmp1, tmp3), split(tmp1, x - 1, tmp1, tmp2);
tmp2 = merge(tree[tmp2].l, tree[tmp2].r);
root = merge(merge(tmp1, tmp2), tmp3);
}
void query_rank(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[tmp1].size + 1);
root = merge(tmp1, tmp2);
}
void query_pre(int x) {
split(root, x - 1, tmp1, tmp2);
printf("%d\n", tree[kth(tmp1, tree[tmp1].size)].val);
root = merge(tmp1, tmp2);
}
void query_suc(int x) {
split(root, x, tmp1, tmp2);
printf("%d\n", tree[kth(tmp2, 1)].val);
root = merge(tmp1, tmp2);
}
int main() {
n = read();
for (int i = 1; i <= n; i++) {
int opt = read(), x = read();
if (opt == 1) insert(x);
if (opt == 2) shan(x);
if (opt == 3) query_rank(x);
if (opt == 4) printf("%d\n", tree[kth(root, x)].val);
if (opt == 5) query_pre(x);
if (opt == 6) query_suc(x);
}
}