笛卡尔树 学习笔记
前置知识
- 知道二叉搜索树的定义;
- 知道堆的定义,知道小根堆和大根堆的特点与区别。
目录
- Part 1:笛卡尔树的定义
- Part 2:笛卡尔树的实现
- Part 3:推荐练习
Part 1:笛卡尔树的定义
笛卡尔树是一种二叉树,每一个结点由一个键值二元组 (k,w) 构成。要求 k 满足二叉搜索树的性质,而 w 满足堆的性质。一个有趣的事实是,如果笛卡尔树的 k,w 键值确定,且 k 互不相同,w 互不相同,那么这个笛卡尔树的结构是唯一的。(OI Wiki)
笛卡尔树是一种非常特殊的二叉搜索树。每个节点有两个信息 (xi,yi),如果只考虑 x,它是一棵二叉搜索树,如果只考虑 y,它是一个小根堆。
Part 2:笛卡尔树的实现
算法简述
在保证 xi 递增的情况下,可以较为轻松地构建一棵笛卡尔树(因为排了序之后,每次插入的新节点都会在这棵树的右链的最后面。所谓右链,即从根节点开始一直向右儿子走,途中经过的节点所组成的链)。
因此,可以用一个单调栈来维护,在出栈的过程中判断,如果当前节点的 x 值严格大于单调栈顶的节点的 x 值,那么就将当前节点作为栈顶节点的右儿子,然后将原本栈顶节点的右子树改为左子树。
代码实现
下面的代码中,i 是 i 节点的 x 值,ai 是 i 节点的 y 值。
st 是单调栈,top 表示单调栈的栈顶元素在单调栈中的下标,lson 和 rson 分别存储了笛卡尔树节点的左右儿子。
inline int build_tree(){
k=top;
for(int i=1;i<=n;++i){
while(k&&a[st[k]]>a[i]) --k;//单调栈
if(k) rson[st[k]]=i;//如果当前节点的x值严格大于单调栈顶的节点的x值,那么就将当前节点作为栈顶节点的右儿子
if(k<top) lson[i]=st[k+1];//将原本栈顶节点的右子树改为左子树
st[++k]=i;//将新结点加入右链
top=k;
}
return st[1];//返回根节点
}