目录
一.引言:
当需要写一种数据结构,来维护一些数
需要提供一下操作
- 插入x数
- 删除x数
- 查询x数的排名
- 查询排位为x的数
- 求x的前驱(小于x的最大数)
- 求x的后继(大于x的最小数)
与我们学过的线代树做比较发现:最复杂的线段树也没有改变树的顺序
所以核心就是动态维护有序序列
二.Binary search tree
BST按照中序遍历(左,根,右)就是从小到大的有序序列
插入
删除
找BST的最大最小值:最大值一直往右走,最小值一直往左走。
找前驱:中序遍历中的前一个位置
操作:如果存在左子树,则左子树的最大值就是前驱,如果不存在左子树找第一个右儿子的父节点。
找后继:中序遍历中的后一个位置
比x大与等于的最小值*lower_bound(x)
找比x的大的最小值*upper_bound(x)
!!!以上的操作set已经实现好了
insert() erase() iterator++,iterator-- , begin() , end()
*lower_bound(x) *upper_bound(x)
出现以上操作不需要手写BST,用set就可以解决,set的底层实现是红黑树
- 查询x数的排名
- 查询排位为x的数
- 比某个数小的最大值
这两个操作set解决不了
三.Balance Tree
常见的平衡树有:AVL树,树堆(Treap),伸展树(Splay tree),红黑树,B树,B+树等
常见的应用于大数据库索引。
树Tree + 堆Heap == Treap!!
首先我们回忆一下堆
堆是一颗完全二叉树,那就满足了”平衡“这个要求!!
但是堆不保证左子节点的值小于本身小于右子节点,所以怎么解决使其称为一个BST?
Hint : 一个随机的BST它的期望高度是logN
所以我们不根据原数据的大小建立堆,因为不能保证BST
不过,我们可以给每个节点再赋一个随机权值,根据这个随机权值构建堆
所以我们写一下每个节点
struct Node{
int l,r; //左孩子和右孩子的编号
int key; //BST中的值
int val; //大根堆中的权值(随机值)
}tr[N];
此时就可以唯一确定一颗BST了,每一步选择子树根的方式是唯一的。
根节点为val的最大值,然后根据根节点的key把剩余节点分为大于key和小于key,子树依次递归
首先在初始化的时候加入两个哨兵-inf和+inf处理边界问题
如何维护呢?
插入很简单,二叉树的插入(一直递归到叶子结点就行)
但是如何进行上调维护平衡呢? 左旋,右旋
对A结点进行左旋与右旋操作:
旋转性质
左旋:左子树的高度+1,右子树的高度-1
右旋:右子树的高度+1,左子树的高度-1
旋转前是BST,旋转后还是BST
如果是左儿子就右旋,右儿子就左旋,直到所在位置满足大根堆
删除操作:通过左旋和右旋操作把要删除的结点转移到叶子结点删除,每次旋转把val大的儿子旋转上来维护大根堆的性质,左孩子大右旋,右孩子大左旋。
总结:通过左旋和右旋维护堆和BST的性质
四.代码实现
节点
struct Node
{
int l, r;
int key, val;
int cnt, size; //size为子树的节点数目
}tr[N];
更新size
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)
{
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);
}
初始化
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);
else if (tr[p].key == key) tr[p].cnt ++ ;
else if (tr[p].key > key)
{
insert(tr[p].l, key);
if (tr[tr[p].l].val > tr[p].val) zig(p);
}
else
{
insert(tr[p].r, key);
if (tr[tr[p].r].val > tr[p].val) zag(p);
}
pushup(p);
}
删除
void remove(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)
{
if (!tr[p].r || tr[tr[p].l].val > tr[tr[p].r].val)
{
zig(p);
remove(tr[p].r, key);
}
else
{
zag(p);
remove(tr[p].l, key);
}
}
else p = 0;
}
else if (tr[p].key > key) remove(tr[p].l, key);
else remove(tr[p].r, key);
pushup(p);
}
通过数值找排名
int get_rank_by_key(int p, int key) // 通过数值找排名
{
if (!p) return 0; // 不存在key
if (tr[p].key == key) return tr[tr[p].l].size + 1;
if (tr[p].key > key) return get_rank_by_key(tr[p].l, key);
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);
if (tr[tr[p].l].size + tr[p].cnt >= rank) return tr[p].key;
return get_key_by_rank(tr[p].r, rank - tr[tr[p].l].size - tr[p].cnt);
}
找到严格小于key的最大数
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));
}
找到严格大于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));
}