Loading

笛卡尔树学习笔记

这种数据结构并不常用,但有时候对简化思维和优化复杂度有一定的帮助。

简介

笛卡尔树是一个二叉树,每一个节点由两个键值构成,\((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;
}

注意,以上实现仅供参考,正确性并不保证。可能会有所修改。

posted @ 2021-11-03 18:38  hyl天梦  阅读(47)  评论(0编辑  收藏  举报