今天听澜神讲了一下平衡树, 来总结一下
常见的平衡树主要有这几种: splay, treap, 红黑树, 替罪羊树等。 他们的本质都是二叉搜索树, 可以在logn的数量级内进行一些操作。
几个常见平衡树的对比
平衡树 | 附加域 | 平衡性 | 效率 | 编程难度 | 实用性 | 特点 |
---|---|---|---|---|---|---|
Treap | 修正值 | 较好 | 较快 | 易 | 好 | 随机平衡 |
Splay | - | 玄学 | 玄学(中) | 中 | 好 | 灵活易变 |
SBT | 子树大小 | 好 | 快 | 中 | 好 | 短小精湛 |
BST | 无 | 差 | 不稳定 | 易 | 一般 | 编写容易 |
treap树
这是一个很玄学的平衡树, 它的平衡由一个dat来维持, 其实就是一个随机生成的数, 这个treap树满足dat的值是小根堆, 这种玄学的操作可以使treap树比较平衡, 两棵子树的大小都差不多。
其中应该是维护大多数平衡树的一个操作就是旋转, 旋转又分为左旋和右旋两种。 这两种操作可以保证平衡树的性质也就是大小关系不会出现错误, 但是可以将平衡树的结构进行调整, 使之成为一棵深度大概为logn的一棵树。
Code:
1 |
|
从网上摘下来的一个板子, 可以满足大部分线段树的一般操作。是一道平衡树板子题的代码。
下面是一些自己的理解与总结:
- 函数参数id要用引用的原因是如果旋转的话, 根的节点编号可能发生改变。在旋转的过程中就直接将根的节点编号直接改变。
- 在很多函数中if(!id)的含义一般是指如果通过递归进入了一个新的节点ch[id][d]那么这个节点内的值一定是0, 所以就可以断定这个节点上没有点 的存在。
- 递归函数直接从根节点进入进行递归, 而不需要递归的函数直接进行查询。
- 旋转可以使平衡树的中序遍历的输出结果不变
splay
splay是一个复杂度比较友好的平衡树, 不会像treap一样玄学。还是通过旋转来实现树的基本的平衡。