笛卡尔树(POJ2201)

题目意思就是让构建一颗笛卡尔树并且输出。本题中“NO”是无意义的,因为笛卡尔树一定能构建出来。

笛卡尔树的基本知识(转自百度百科):

笛卡尔树又称笛卡儿树,在数据结构中属于二叉树的一种。
可以这么说:笛卡尔树是一棵二叉树,树的每个节点有两个值,一个为key,一个为value。光看key的话,笛卡尔树是一棵二叉搜索树,每个节点的左子树的key都比它小,右子树都比它大;光看value的话,笛卡尔树有点类似堆,根节点的value是最小(或者最大)的,每个节点的value都比它的子树要小(或者大)。
无相同元素的数列构造出的笛卡尔树具有下列 性质
1、结点一一对应于数列元素。即数列中的每个元素都对应于树中某个唯一结点,树结点也对应于数列中的某个唯一元素
2、中序遍历(in-order traverse)笛卡尔树即可得到原数列。即任意树结点的左子树结点所对应的数列元素下标比该结点所对应元素的下标小,右子树结点所对应数列元素下标比该结点所对应元素下标大。
3、树结构存在堆序性质,即任意树结点所对应数值大(或小)于其左、右子树内任意结点对应数值(即根节点为其子树的最值)
根据堆序性质,笛卡尔树根结点为数列中的最大/小值,树本身也可以通过这一性质递归地定义:根结点为序列的最大/小值,左、右子树则对应于左右两个子序列,其结点同样为两个子序列的最大/小值。因此,上述三条性质唯一地定义了笛卡尔树。若数列中存在重复值,则可用其它排序原则为数列中相同元素排定序列,例如以下标较小的数为较小,便能为含重复值的数列构造笛卡尔树。

代码实现以及代码解释看注释吧,写的比较详细了,感谢霜刃未曾试的讲解

#include <cstdio>
#include <algorithm>
#include <iostream>
using namespace std;

const int N = 50000 + 10 , INF = 0x3f3f3f3f;

int root;

struct node
{
    int val , pri , fat , id , son[2];// val遵守二叉搜索树规则,pri遵守堆的规则
    friend bool operator < (node a , node b)
    {
        return a.val < b.val;
    }
    void init(int val_ , int pri_ , int fat_ , int id_)
    {
        val = val_ , pri = pri_ , fat = fat_ , id = id_;
        son[0] = son[1] = 0;
    }
}tr[N];

int stk[N] , top;
int ans_fat[N] , ans_l[N] , ans_r[N];

int car_build(int n)
{
    top = 0;
    for(int i = 1 ; i <= n ; i++)
    {
        int k = top;//k是在栈上移动的指针
        while(k > 0 && tr[stk[k-1]].pri > tr[i].pri) k--; // 寻找第一个小于当前元素的节点x
        if(k != 0)//如果栈中还有元素
        {
            tr[i].fat = stk[k-1]; //当前点的father变为x
            tr[stk[k-1]].son[1] = i;//当前节点插入成为x的右子节点
        }
        if(k != top)/*如果有element曾经出栈,那么需要将出栈的最后一个变成当前点的左
            子节点,因为出栈的所有element是一条链,只需要将最小的那个元素重新连接
            到树上就把整条链都连接上了*/
        {
            tr[stk[k]].fat = i; //出栈的最后一个的father变成当前节点
            tr[i].son[0] = stk[k];//当前节点的左节点变成最后一个出栈的节点
        }
        stk[k++] = i; // 右链的长度也因为当前点的插入而变成(当前点插入位置的深度+1)
        top = k;
    }
    return stk[0];//插入结束时右链的栈底的元素就是根
}

void dfs(int x ,int fat)
{
    if(! x) return;
    ans_fat[tr[x].id] = tr[fat].id;
    ans_l[tr[x].id] = tr[tr[x].son[0]].id;
    ans_r[tr[x].id] = tr[tr[x].son[1]].id;
    dfs(tr[x].son[0], x);
    dfs(tr[x].son[1], x);
}
int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        int a , b;
        tr[0].init(0,0,0,0);
        for(int i = 1 ; i <= n ; i++)
        {
            scanf("%d%d",&a,&b);
            tr[i].init(a,b,0,i);
        }
        sort(tr + 1, tr + 1 + n);
        root = car_build(n);
        dfs(root, 0);
        puts("YES");
        for(int i = 1; i <= n; i++) printf("%d %d %d\n", ans_fat[i], ans_l[i], ans_r[i]);
    }
    return 0;
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值