笛卡尔树

来源:互联网 发布:端口转发手机软件 编辑:程序博客网 时间:2024/04/29 11:23
笛卡尔树性质:
1.树中的元素满足二叉搜索树性质,要求按照中序遍历得到的序列为原数组序列(左儿子的key值小于自己,右儿子的key值大于自己)

2.树中节点满足堆性质,节点的val值要大于其左右子节点的val值


整个过程的第一步是把所有点按照key排序,然后从一个节点开始,按key递增顺序依次插入节点。想象一下,假设已经有一棵笛卡尔树,那么现在我们要插入一个新的节点,而这个节点比这棵树所有节点的key都大,那么应该如何插入呢?假设这个节点已经被插入,那么它的位置肯定是在从根节点开始一直向右走。所以,每次插入新节点的时候,一定插入到最右侧那条路中的某个位置,而原来位置的节点变成了这个新节点的左子树,新插入的点变成最右侧那条路的最后一个节点。

那么如何确定插入的位置呢?那就要根据这个节点的value值了,因为满足堆的性质,所以一条路从上到下,其value值肯定是递减的。就是因为这个递减的性质,我们可以把最右侧的那条路用一个栈表示,栈底是根,栈顶是最新节点,从底到顶,value值和key值都递增。每次新插入一个节点的时候,就从顶往底一个个看,找到第一个value大于新节点value的节点,作为新节点的父亲即可。因为每个节点最多进栈一次,出栈一次,所以整个构树过程是O(N)的。

附上代码(ZOJ-1243):

#include<bits/stdc++.h>using namespace std;const int MX = 5e4 + 5;struct node {    char str[105];    int val;    int lson, rson, fa;} T[MX];bool cmp(const node& p1, const node& p2) {    return strcmp(p1.str, p2.str) < 0;}int st[MX], top;void init(int n) {    top = 0;    for (int i = 0; i <= n; i++) T[i].lson = T[i].rson = T[i].fa = 0;    T[0].val = 0x3f3f3f3f;    st[++top] = 0;}void Insert(int i) {    while (top > 0 && T[st[top]].val < T[i].val) top--;    T[i].fa = st[top];    T[i].lson = T[st[top]].rson;    T[T[st[top]].rson].fa = i;    T[st[top]].rson = i;    st[++top] = i;}void print(int rt) {    if (rt == 0) return;    printf("(");    print(T[rt].lson);    printf("%s/%d", T[rt].str, T[rt].val);    print(T[rt].rson);    printf(")");}int main() {    //freopen("in.txt", "r", stdin);    int n;    while (scanf("%d", &n), n) {        init(n);        for (int i = 1; i <= n; i++) scanf(" %[a-z]/%d", T[i].str, &T[i].val);        sort(T + 1, T + n + 1, cmp);        for (int i = 1; i <= n; i++) Insert(i);        print(T[0].rson);        printf("\n");    }    return 0;}


原创粉丝点击