题目:
笛卡尔树:笛卡尔树中的每个节点有两个数据域k,a,对于数据域k,满足二叉搜索树性质,对于数据域a,满足最小堆性质。
给出N个节点,1<=N<=50000,每个节点由一对k,a构成,判断能否根据这些节点构建一颗笛卡尔树,如果可以构建则输出构造出的笛卡尔树,否则输出“NO”。
解题思路:
首先,根据N个节点,肯定可以构造出一颗笛卡尔树。
方法1:
递归法 (经过验证,该方法会超时) (见代码1.)
a) 对节点数组,选出a值最小的节点,该节点为根节点
b) 以根节点的k值为枢纽对数组进行划分,小于k的节点位于根节点左边,大于k的节点位于根节点右边
c) 根节点左边的节点为根节点的左子树中的节点,右边的节点为右子树中的节点,对根节点左边的节点递归构建左子树,对根节点右边的节点递归构造右子树。
方法2:
节点逐个插入法,(见代码2)
a) 对节点数组按k值从小到大排序。
b) 令第一个节点为根节点,从第二个节点开始遍历数组,将节点逐个插入树中
将节点插入树中的步骤为:
从当前待插入节点的前一个节点开始,自底向上找出一个节点,使得该节点的a值比当前节点的a值小。
如果没找到:
则当前节点为新的根节点,原来的根节点改为当前节点的左孩子。
如果找到:
设找到的节点标号为i,则i节点原来的右孩子改为当前节点的左孩子,当前节点为节点i的右孩子。
c) 插入结束后输出结果
代码1:递归法
#include<stdio.h>
typedef struct
{
int k,a,num;//num从1开始编号
} Node;
Node data[50000];
int father[50001];
int left_child[50001];
int right_child[50001];
int n;
/*找出data数组中,从下标s开始到下标e之间辅助域a最小的点
返回该点在数组中的下标 */
int min_node(int s, int e)
{
int min_index,min_a;
min_index=s;
min_a=data[s].a;
while(++s<=e)
if(data[s].a < min_a)
{
min_index=s;
min_a=data[s].a;
}
return min_index;
}
void swap(Node *a, Node *b)
{
Node temp;
temp = *a;
*a = *b;
*b = temp;
}
//以index处的元素为枢纽划分数组,返回枢纽的下标
int partition(int s, int e, int index)
{
int i,j;
Node temp,pivot;
//交换枢纽元素和开始元素
swap(&data[s], &data[index]);
i=s;
j=s+1;
pivot=data[s];
while(j<=e)
{
if(data[j].k < pivot.k)
{
i++;
swap(&data[i], &data[j]);
}
j++;
}
swap(&data[s], &data[i]);
return i;
}
/*为data数组下标从s到e的部分建立笛卡尔树
* s:开始下标, e:结束下标
* child:当前构建的笛卡尔树的根节点为父节点的孩子
* p: 当前构建的笛卡尔树的根节点的父节点编号
*/
void built(int s, int e, int child[], int p)
{
int min_index, pivot_index;
if(s>e) return;
//找出辅助域a最小的节点,该节点为该子树的根节点
min_index=min_node(s,e);
father[data[min_index].num]=p;
child[p] = data[min_index].num;
//对数组以根节点的K值为枢纽进行划分
pivot_index = partition(s,e,min_index);
//构建左子树
built(s, pivot_index-1, left_child, data[pivot_index].num);
//构建右子树
built(pivot_index+1, e, right_child, data[pivot_index].num);
}
int main()
{
int i;
scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%d %d", &data[i].k, &data[i].a);
data[i].num=i+1;
}
//构建笛卡尔树
built(0, n-1, left_child, 0); //第3个参数随意,也可以为right_child
//打印结果
printf("YES\n");
for(i=1; i<=n; i++) printf("%d %d %d\n", father[i], left_child[i], right_child[i]);
return 0;
}
#include<stdio.h>
typedef struct
{
int k,a,num;//num从1开始编号
} Node;
Node data[50000];
int father[50001];
int left_child[50001];
int right_child[50001];
int convert[50001];
int n;
int cmp(const void *a, const void *b)
{
Node *p1,*p2;
p1=(Node*)a;
p2=(Node*)b;
return p1->k - p2->k;
}
//插入一个节点,index为节点在data数组中的下标
void insert(int index)
{
int i,j;
i=data[index-1].num;
//找到域a比当前节点小的节点
while(i!=0 && data[convert[i]].a > data[index].a) {j=i;i=father[i];}
if(i==0)
{
//没有找到这样的节点,则当前节点为根节点,前一个节点为当前节点的左孩子
father[j]=data[index].num;
left_child[data[index].num] = j;
}
else
{
//找到了这样的节点i,则当前节点为i的右孩子,i节点原来的右孩子改为当前节点的左孩子
left_child[data[index].num]=right_child[i];
father[right_child[i]]=data[index].num;
right_child[i]=data[index].num;
father[data[index].num]=i;
}
}
int main()
{
int i;
scanf("%d", &n);
for(i=0; i<n; i++)
{
scanf("%d %d", &data[i].k, &data[i].a);
data[i].num=i+1;
}
//对节点按a从小到大排序
qsort(data, n, sizeof(Node), cmp);
//对节点依次插入树中
convert[data[0].num]=0;
for(i=1; i<n; i++) convert[data[i].num]=i, insert(i);
//打印结果
printf("YES\n");
for(i=1; i<=n; i++) printf("%d %d %d\n", father[i], left_child[i], right_child[i]);
return 0;
}