笛卡尔树
笛卡尔树图例(该笛卡尔树遵循根节点小于叶子节点)
笛卡尔树的性质
无相同元素的数列构造出的笛卡尔树具有下列性质:
1.结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素。
2.中序遍历(in-order traverse)笛卡尔树即可得到原数列。
3.树结构存在堆序性质,即任意树结点所对应数值大/小于其左、右子树内任意结点对应数值
代码思路
(大根堆)
对于一个没有相同元素的数组 a[n]
,设定一个数组stk[n]
存储数组元素的下标,设定top
为stk[n]数组已经存储的元素数。用来首先从a[n]选取第一个元素成为第一个节点,并将其下标存入至stk[n]中。接下来从a[n]选取下一个元素a[i]
,观察stk[n]数组,找到stk[k]
,使得a[stk[k]]<a[i]&&a[stk[k - 1]]>a[i]
,于是让stk[k]=i
,(若不存在stk[k]对应的元素大于a[i],则让查找到的k为0,若不存在元素小于a[i],则查找到的k即为top)接下来让stk[n]数组中下标大于k的元素全部失效(即失去实际意义,代码中通过top
的移动完成,类似于栈)利用上述操作,可以让stk中存储的下标所对应的数组元素保持降序,所以观察stk[n]数组的那一步只要通过while语句即可以实现。
构建树的操作,即在上述中完成k
的查找之后,将 a[stk[k]]的父节点设定为a[i],将a[i]设定为a[stk[k-1]]的右子节点,将a[stk[k-1]]原先的右子节点设定为a[i]的左子节点。
构造代码
int l[maxn], r[maxn], vis[maxn], stk[maxn];
int n; //数组长度
int a[maxn];
int build() {
//大根堆,即树结点大于左右子数节点
int top = 0;
for (int i = 1; i <= n; i++) l[i] = r[i] = vis[i] = 0; //初始化l[]、r[]、vis[]
for (int i = 1; i <= n; i++) {
int k = top; //top为栈顶(可插入元素的位置)
while (k > 0 && a[stk[k - 1]] < a[i]) --k; //找到 a[stk[k - 1]] > a[i],stk[]保存的是下标
if (k) r[stk[k - 1]] = i; //让a[i]成为a[stk[k - 1]]的右子树
if (k<top) l[i] = stk[k]; //当k<top时,说明从栈顶到栈底有元素比新的元素小,由前面while可知该元素下标为stk[k]
//可能有多个小于a[i]的元素,下标为stk[k]的元素最大
stk[k++] = i;
top = k;
}
for (int i = 1; i <= n; i++) vis[l[i]] = vis[r[i]] = 1;
int ret = 0;
for (int i = 1; i <= n; i++) { if (vis[i] == 0) ret = i; }
return ret;
}
代码源:https://blog.csdn.net/luyehao1/article/details/81280093
备注: 在运用该代码时,数组a[n]
的第一个元素不利用,即从a[1]
开始存元素。