简易实现普通平衡树
介绍:✨
大家可以把平衡树简略的理解为可以级别复杂度内实现这么几种操作的数据结构
- 插入一个数
- 删除一个数
- 查询一个数的排名
- 查询排名为x的数
- 求x的前驱
- 求x的后继
我们知道这些就是普通的平衡树所支持的操作
而且时间复杂度几乎都是级别的
困扰:😫
大家都知道市面上有很多平衡树的分类:
- AVL
- 红黑树
- splay
- 有/无旋treap(FHQtreap等)
等等等等
无一例外的常数略大,代码难写。
有没有可能有一种比较好写的方案,并且常数也十分优秀呢?
当然是有的,如果你的需求只有平衡树的上述基础操作,不妨试试我的解决方案吧~
引出主角:😃
那么我们就要引出今天的主角:Fenwick---树状数组
树状数组我们就不仔细介绍了,这是一个比较基础的数据结构。
简单说一下就是这个数据结构可以单点修改区间查询。
最最最最重要的一点是树状数组代码敲好写,常数敲小,难道这不就是我们所追求的嘛~
解决:😎
在这之前你可能要稍微了解一下:
- 树状数组的原理
- 下标为值域的树状数组
- 树状数组如何树上二分
- 值域离散化
在这之后我们就可以对每一个操作来剖析了
- 插入和删除
- 这个非常好解决,只要对离散化后的下标加减一就可以了
- 查询一个数的排名
- 也非常好解决,只需要查询比他小的索引和+1就可以了
- 查询排名为x的数
- 这个需要使用到我们的树上二分,就需要你对树状数组有比较深的理解(一会可以看代码)
- 求x的前驱后继
- 也非常简单,就是查询比他排名小一位和大一位的数,然后再用这个排名询问值
代码:🚙
template <typename T>
struct Fenwick {
const int n;
std::vector<T> a;
Fenwick(int n) : n(++n), a(n) {}
void add(int x, T v) {
for (int i = x; i <= n; i += i & -i) {
a[i] += v;
}
}
T sum(int x) {
T ans = 0;
for (int i = x; i > 0; i -= i & -i) {
ans += a[i];
}
return ans;
}
T rangeSum(int l, int r) {
return sum(r) - sum(l - 1);
}
T rank(int p)
{
return sum(p - 1) + 1;
}
T val(int rk)
{
int _log = ((ceil(log(n) / log(2))));
int pos = 1 << _log;
for (int i = _log - 1; ~i; i -- )
if (a[pos - (1 << i)] >= rk) pos -= (1 << i);
else rk -= a[pos - (1 << i)];
return pos;
}
T pre(int p)
{
return val(rank(p) - 1);
}
T nxt(int p)
{
return val(rank(p + 1));
}
};
功能代码也就是30来行,有没有颠覆你对平衡树的认知呢?
树状数组代码初模板来自国内顶尖高手jiangly 致敬我的偶像蒋ls~
在这个基础上由我进行封装成了现在这样
这样一个平衡树的基本功能就用一个更好的方式来实现了~
平衡树模板题 可以拿去试试跑的速度了,快的狠呢~
那我们下一篇有趣的数据结构实现方式再见咯😄