POJ 2201 Cartesian Tree 笛卡尔树

题目:

笛卡尔树:笛卡尔树中的每个节点有两个数据域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;
}


代码2:逐个插入法

#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;
}


你可能感兴趣的:(POJ 2201 Cartesian Tree 笛卡尔树)