笛卡尔树学习笔记
这种数据结构并不常用,但有时候对简化思维和优化复杂度有一定的帮助。
简介
笛卡尔树是一个二叉树,每一个节点由两个键值构成,\((k,w)\) 其中,\(k\) 满足二叉搜索树的性质,\(w\) 满足二叉堆的性质,
如果给定数组 \(a\),以数组下标作为 \(k\),用数组里面存放的值作为 \(w\),则不难发现这个笛卡尔树的性质,一棵子树内的下标对应一段连续的区间,当然我们建立笛卡尔树不能只是对数组建立,这样具有一定的特殊性,我们要对于任意的二元组建立笛卡尔树。
构建
对任意二元组建立笛卡尔树的方法如下:
-
首先,我们将所有的二元组按照 \(k\),也就是第一关键字来排序。
-
我们定义 右链 为从根节点开始,如果有右儿子就一直走下去,走过的点形成的链。
我们考虑这样 增量 来构造笛卡尔树,设当前的二元组为 \((a,b)\)。
-
我们从右链的最低端开始遍历,设当前遍历的二元组为 \((c,d)\),如果我们有 \(b<d\) 那么就停止遍历,将插入点设置为当前遍历的 右 子树,把插入点的左子树设置为当前遍历点以前的右儿子。
考虑正确性证明:
- 首先关注到 \(k\) 是有序的,因为我们是把当前遍历点设置为插入点的 左 子树,所以这样显然满足二查搜索树性质,又因为我们是遍历到有 \(b<d\) 的时候才插入,所以很显然这棵树的第二关键字也满足堆的性质。
考虑复杂度证明:
- 首先显然,如果一个当前插入点遍历到了一个点却没有选择插入,那么这个点以后就不会存在在右链之中,也就是说,每个点仅会被最多 经过 一次,所以复杂度是均摊 \(O(n)\)。
考虑实现,我们可以用栈来模拟右链,插入代码如下:
inline void Insert(int k){
int Last=Top;
while(W[Stack[Last]]>W[k]) Last--;
ls[k]=rs[Last]
rs[Last]=k;
Stack[++Last]=k;
Top=k;
}
注意,以上实现仅供参考,正确性并不保证。可能会有所修改。