参考:link
定义
笛卡尔树是一种二叉搜索树的数据结构。树的每个节点有两个值,分别为key 和 value。
Key类似于二叉搜索树,每个节点的左子树key值都要小于其本身,右子树的key值都比它大
value类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要小(或者大)。
(图片来自维基百科)
性质
1.结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素
2.树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列
3.树中节点满足堆性质,节点的value值要大于其左右子节点的vlaue值
构造
满足二叉搜索树的性质
当按照key从1到n的顺序将数组中的每个元素插入到笛卡尔树中时,当前要被插入的元素的key值最大,因此根据二叉搜索树的性质需要沿着当前已经完成的笛卡尔树的根的右子树链搜索。
满足堆的性质
最大堆
父节点的value值要大于子节点的value值,所以沿着树根的右子树链往下走,直到搜索到的节点的value值小于等于当前要插入节点的value值。
如将2插入到树,其右子树链为
5->4>3>1
中,从5开始找,直到找到1,1的位置就是当前节点要插入的位置,记为Pos, 将当前节点插入到Pos的位置,同时将以Pos为根的子树1挂载到新插入的节点的左子树
最小堆
父节点的value值要小于子节点的value值,所以沿着树根的右子树链往下走,直到搜索到的节点的value值大于等于当前要插入节点的value值。
如将4插入到树,其右子树链为
1->2->3->5
中,从1开始找,直到找到5,5的位置就是当前要插入的位置,记为Pos, 将当前节点插入到Pos的位置,同时将以Pos为根的子树5挂载到新插入的节点的左子树
用栈实现
整个过程中可以用栈维护。栈中保存当前树中的从树根开始的右子节点链,根在栈底部。
插入新元素的时候,从树的右子链的最末尾从下往上查找,直到找到第一个满足堆性质的节点(即找到的节点的value值大于(最大堆)当前需要插入的节点)。
用栈来实现就是从栈顶不断弹出元素,直到栈顶的元素的value大于(最大堆)当前结点的value,然后将该节点入栈,同时将最后被弹出的节点的父亲指向该节点,以及该节点的左子节点指向最后被弹出的节点。
时间复杂度
由于每个元素都只入栈、出栈一次,所以时间复杂度是线性的O(n)
code
#include<vector>
//构造笛卡尔树 最小堆
vector<int> cartesianTree(int array[],int n) {
vector<int> result(n);
vector<pair<int, int> > stack;//pair存节点的值和下标,stack存树的右子树链
stack.push_back(pair<int,int>(0, -1));
int a;
for (int i = 0, a; i < n; ++i) {
a = array[i];//当前需插入的节点
while (stack.back().first > a) {//从栈的顶部往下找,找到第一个小于当前节点的节点(于当前节点的就出栈)
stack.pop_back();
}
result[i] = stack.back().second;//存当前节点父节点的下标
stack.push_back(pair<int,int>(a, i));//当前节点入栈,成为其子节点
}
return result;
}