笛卡尔树知识点+思路
笛卡尔树性质:
每个结点存储 (k,w) ,即一个序偶
-
对于k而言,是一个二叉搜索树(满足条件:根节点大于左节点,小于右节点)
-
对于w而言,是一个小根堆/大根堆(满足条件:根节点值小于/大于左,右节点值)
笛卡尔树不是平衡的
笛卡尔树的构建:
如何使一个数组->笛卡尔树?
单调栈O(n),处理出每一个结点的左儿子,右儿子,权值w

举例:对于上图的数组 a=[9, 3, 7, 1, 8, 12, 10, 20, 15, 18, 5],构建一颗小根堆笛卡尔树
显然序偶 (k,w) = (元素下标,元素大小)
我们使单调栈元素是单调递增的
从左至右:显然元素下标是单调增的,所以为了满足二叉搜索树的条件,我们首先会把这个结点视为右结点(因为前面元素下标都比该结点小)
然后通过 维护的单调栈 判断这个结点是哪一个结点的右结点
-
如果单调栈为空,那么该结点是初始结点,不用判断左右结点情况
-
如果单调栈非空:
若栈顶元素大小大于该元素大小,由于我们需要维护小根堆,显然当前结点不能是栈顶结点的儿子。直接出栈
若栈顶元素大小小于该元素大小,那么我们直接把当前结点看作栈顶结点的右结点
同时,我们需要维护出栈的最后一个元素结点,我们将该元素结点看作当前结点的左结点(元素大小既大于当前结点,元素下标又小于当前结点)
特别的,如果不断出栈,最后栈为空,说明当前结点看作根结点
最后将当前结点入栈
参考代码
struct node{
int l=0,r=0,fa=0;
int val;
};
int n;cin>>n;
vector<int>a(n+1);rep(i,1,n)cin>>a[i];
vector<node>tr(n+1);
stack<int>stk;
rep(i,1,n){
int L=0,R=0;
while(stk.size()&&a[stk.top()]>a[i]){
L=stk.top();
stk.pop();
}
if(L!=0){
tr[L].fa=i;
if(stk.size())tr[stk.top()].r=i;
}
else if(stk.size()){
tr[stk.top()].r=i;
}
stk.push(i);
tr[i].val=a[i];
tr[i].l=L;
tr[i].r=R;
}

浙公网安备 33010602011771号