笛卡尔树——神奇的“二叉搜索堆”

笛卡尔树是一种同时满足二叉搜索树(点关系)堆(点大小)的性质的数据结构。它的中序遍历为原数组,且点的权值比它的孩子大(或小)。

其实可以想象一下笛卡尔树与区间的关系。最顶上的点A就是整个区间的最大值的点,它的左孩子都在它的左边(区间上),右孩子在它区间的右边,然后左子树中,又是最大值的点是根,它的左孩子在它的左边(区间上),右孩子在它的右边.......不断如此。

建立这棵树我们只要用一个单调栈来维护这棵笛卡尔树的最右边的节点。

我们按照点的标号index 1-n不断插入节点,因为它的index最大,所以根据二叉搜索的性质应该要从这棵树的最右边插入。

又要满足堆的性质,所以我们插入一个点A的时候,沿这棵树的最右节点不断向上找,直到找到第一个权值比它大的点B,根据标号,A的左孩子就是B的右孩子,而A就是B的右孩子了(这实际上似乎就是某种旋转)

实际实现的时候我们可以用栈维护这棵树的最右子树,不断弹出顶部节点以便找到第一个权值比它大的点,再更改关系即可。由于每个点都只能进出一次栈,所以复杂度为O(n).

 1 #include<iostream>
 2 #include<cstring>
 3 #include<cstdio>
 4 #define N 3000002
 5 using namespace std;
 6 int n,m,zhan[N],len;
 7 struct data1{
 8     int l,r,p,v,id;  //l左孩子,r右孩子,p父亲,v权值,id标号 
 9     void init(){
10         l=0,r=0,p=0,v=0,id=0;
11     }
12 }tree[N];
13 int build(){     //建笛卡尔树 
14     len=1;
15     zhan[1]=1;   //栈里面存的是点的标号Index 
16     for (int i=2;i<=n;i++){
17         while ((len>0)&&(tree[zhan[len]].v<tree[i].v)) len--;  
18         if (len){
19             tree[i].p=zhan[len];            
20             tree[tree[zhan[len]].r].p=i;   
21             tree[i].l=tree[zhan[len]].r;
22             tree[zhan[len]].r=i;
23         }     //用纸比划比划能够更好自己理解这代码 
24         else {
25             tree[zhan[1]].p=i;
26             tree[i].l=zhan[1];
27         }
28         zhan[++len]=i;    
29     }
30     return zhan[1];
31 }
32 int main(){
33     scanf("%d%d",&n,&m);
34     for (int i=1;i<=n;i++){
35         tree[i].init();
36                 tree[i].id=i;
37         scanf("%d",&tree[i].v);
38     }
39     return 0;
40 }
神奇的代码

 

posted @ 2017-08-09 12:14  ~Lanly~  阅读(1787)  评论(0编辑  收藏  举报