小知识: C + + 的 S T L 中的 s e t 容器内部封装为平衡树 小知识:C++的STL中的set容器内部封装为平衡树 小知识:C++的STL中的set容器内部封装为平衡树
动态维护一个有序序列 动态维护一个有序序列 动态维护一个有序序列
注: S T L 中的 s e t 也支持前 4 个操作 注:STL中的set也支持前4个操作 注:STL中的set也支持前4个操作
让 B S T 变得随机 , 从而避免使时间复杂度维持为 O ( l o g n ) 让BST变得随机, 从而避免使时间复杂度维持为O(logn) 让BST变得随机,从而避免使时间复杂度维持为O(logn)
旋转操作 旋转操作 旋转操作
旋转操作的含义: 旋转操作的含义: 旋转操作的含义:
下面各操作看代码实现即可:
代码及注释如下:
#include
#include
#include
using namespace std;
const int N = 100010, INF = 1e8;
int n;
struct Node
{
int l, r;
int key, val; // 实际的值 堆(大根堆)中的编号(便于打乱)
int cnt, size; // 此数的数量 该子树中的数的数量
}tr[N];
int root, idx;
// 子节点更新父节点信息
void pushup(int p)
{
tr[p].size = tr[tr[p].l].size + tr[tr[p].r].size + tr[p].cnt;
}
// 创建新节点
int get_node(int key)
{
idx ++;
tr[idx].key = key;
tr[idx].val = rand();
tr[idx].cnt = tr[idx].size = 1;
return idx;
}
void zig(int &p) // 右旋(将根节点变为左子树)
{
int q = tr[p].l;
tr[p].l = tr[q].r, tr[q].r = p, p = q;
pushup(tr[p].r), pushup(p);
}
void zag(int &p) // 左旋(将根节点变为右子树)
{
int q = tr[p].r;
tr[p].r = tr[q].l, tr[q].l = p, p = q;
pushup(tr[p].l), pushup(p);
}
// 初始化根节点,同时初始化两个哨兵 -INF 和 INF
void build()
{
get_node(-INF), get_node(INF);
root = 1, tr[1].r = 2;
pushup(root);
if(tr[1].val < tr[2].val) zag(root);
}
void insert(int &p, int key)
{
if(!p) p = get_node(key); // 树种节点为0,创建新节点即可
else if(tr[p].key == key) tr[p].cnt ++; // 子树的根节点恰好为key,令 key 的 cnt ++ 即可
else if(tr[p].key > key)
{
insert(tr[p].l, key); // 递归插入key
// 若新插入的左子树的key > 根节点的key则,右旋,令左子树为根
if(tr[tr[p].l].val > tr[p].val) zig(p);
}
else
{
insert(tr[p].r, key); // 递归插入key
// 若新插入的右子树的key > 根节点的key则,左旋,令右子树为根
if(tr[tr[p].r].val > tr[p].val) zag(p);
}
pushup(p);
}
// 一直左旋或右旋直到叶子节点,然后删去即可
void remove0(int &p, int key)
{
if(!p) return ;
if(tr[p].key == key)
{
if(tr[p].cnt > 1) tr[p].cnt --;
else if(tr[p].l || tr[p].r)
{
// 若右节点不存在,或左节点在堆的val > 右节点的val
if(!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
{
// 先右旋,使左节点为根节点保证大根堆
zig(p);
// 此时根节点旋转至右节点,原函数等效于删除右节点
remove0(tr[p].r, key);
}
else // 若左节点不存在,或右节点在堆的val > 右节点的val
{
// 先左旋,使右节点为根节点保证大根堆
zag(p);
// 此时根节点旋转至左节点,原函数等效于删除左节点
remove0(tr[p].l, key);
}
}
else p = 0; // 找到叶子节点p,令其 == 0 即为删除
}
else if(tr[p].key > key) remove0(tr[p].l, key); // 没找到递归左子树
else remove0(tr[p].r, key); // 没找到递归右子树
pushup(p);
}
int get_rank_by_key(int p, int key) // 根据值求排名
{
if(!p) return 0; // 本题不存在此情况
if(tr[p].key == key) return tr[tr[p].l].size + 1;
else if(tr[p].key > key) return get_rank_by_key(tr[p].l, key);
else return tr[tr[p].l].size + tr[p].cnt + get_rank_by_key(tr[p].r, key);
}
int get_key_by_rank(int p, int rank) // 根据排名求值
{
if(!p) return INF;
if(tr[tr[p].l].size >= rank) return get_key_by_rank(tr[p].l, rank);
else if(tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
else return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}
int get_prev(int p, int key) // 找到严格小于key的数
{
if(!p) return -INF;
if(tr[p].key >= key) return get_prev(tr[p].l, key);
return max(tr[p].key, get_prev(tr[p].r, key));
}
int get_next(int p, int key) // 找到严格大于key的数
{
if(!p) return INF;
if(tr[p].key <= key) return get_next(tr[p].r, key);
return min(tr[p].key, get_next(tr[p].l, key));
}
int main()
{
build();
scanf("%d", &n);
int opt, x;
for (int i = 1; i <= n; i ++ )
{
scanf("%d%d", &opt, &x);
if (opt == 1) insert(root, x);
else if (opt == 2) remove0(root, x);
else if (opt == 3) printf("%d\n", get_rank_by_key(root, x) - 1); // 初始化哨兵-INF,要 - 1
else if (opt == 4) printf("%d\n", get_key_by_rank(root, x + 1)); // 有哨兵-INF,要 + 1
else if (opt == 5) printf("%d\n", get_prev(root, x));
else printf("%d\n", get_next(root, x));
}
return 0;
}