笛卡尔树是一种特殊的二叉树,其结点包含两个关键字K1和K2。首先笛卡尔树是关于K1的二叉搜索树,即结点左子树的所有K1值都比该结点的K1值小,右子树则大。其次所有结点的K2关键字满足优先队列(不妨设为最小堆)的顺序要求,即该结点的K2值比其子树中所有结点的K2值小。给定一棵二叉树,请判断该树是否笛卡尔树。
输入格式:
输入首先给出正整数N(≤1000),为树中结点的个数。随后N行,每行给出一个结点的信息,包括:结点的K1值、K2值、左孩子结点编号、右孩子结点编号。设结点从0~(N-1)顺序编号。若某结点不存在孩子结点,则该位置给出−1。
输出格式:
输出YES如果该树是一棵笛卡尔树;否则输出NO.
输入样例1:
6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 21 -1 4
15 22 -1 -1
5 35 -1 -1
输出样例1:
YES
输入样例2:
6
8 27 5 1
9 40 -1 -1
10 20 0 3
12 11 -1 4
15 22 -1 -1
50 35 -1 -1
输出样例2:
NO
分析:
这题考的是二叉搜索树和堆的概念,在二叉搜索树中,若它的左子树或右子树不为空的话,它要大于左子树的最大值和右子树上的最小值,因为这里我们不知道所给K1的符不符合二叉搜索树,所以我们不仅要和左右儿子比较大小,还要和属于左子树中最大值的位置上的节点和右子树中最小值的位置比较大小(防止出现K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件的情况)。堆的话,验证起来相对简单很多,就与左右子树比较大小就行。
然后还有一个可以姑且说为难一点点(其实就是生疏)的地方就是,根据用户输入的大小来动态申请个二维数组的空间。在这里用二级指针就可以做到,其本质就是套娃(我申请了空间来存放一级指针,而一级指针又去申请存放它所能指的类型的空间)。
我的代码:
#include<stdio.h>
#include<stdlib.h>
#define K1 0
#define K2 1
int** CreateTree(int Size);
void Put(int**Tree,int Size);
int Judge(int**Tree,int Size);
int GetMax(int**Tree,int LT);
int GetMin(int**Tree,int RT);
int main(void)
{
int Size,result;//二维数组的行数,其是否是笛卡尔树的结果
int** Tree;//一个待会儿用来创建二维数组的二级指针
scanf("%d",&Size);
Tree=CreateTree(Size);//创建好二维数组并返回其首地址
Put(Tree,Size);//放入节点
result=Judge(Tree,Size);//判断是否符合条件
if(result) printf("YES");
else printf("NO");
return 0;
}
/*用二级指针创建二维数组*/
int** CreateTree(int Size)
{
int** Tree;
Tree=(int**)malloc(sizeof(int*)*Size);//此时已经创好一个存放一级指针的数组
for(int i=0;i<Size;i++)//每个一级指针都分别指向一个大小相同的数组
Tree[i]=(int*)malloc(sizeof(int)*4);
return Tree;//将首地址返回
}
/*放入节点数据*/
void Put(int**Tree,int Size)
{
int Data;
/*挨个录入每个节点的数据*/
for(int i=0;i<Size;i++)
for(int j=0;j<4;j++)
{
scanf("%d",&Data);
Tree[i][j]=Data;
}
}
/*判断该树是否是笛卡尔树*/
int Judge(int**Tree,int Size)
{
int result=1;//先假设该树是笛卡尔树
for(int i=0;i<Size;i++)//将节点挨个取出来判断
{
int L,R,Max,Min;//左子树的位置,右子树的位置,左子树中的最大值,右子树中的最小值
L=Tree[i][2];//取出该节点左子树的位置
R=Tree[i][3];//取出该节点右子树的位置
if(L!=-1&&R!=-1)//如果这个节点左右子树都不为空
{
Max=GetMax(Tree,L);//先获得左子树中的最大值
Min=GetMin(Tree,R);//再获得右子树中的最小值
if(Tree[i][K1]<Tree[L][K1]||Tree[i][K1]>Tree[R][K1]||Max>Tree[i][K1]||Tree[i][K2]>Min)//先看看它K1值符不符合二叉搜索树的要求
{
result=0;
break;
}
if(Tree[i][K2]>Tree[L][K2]||Tree[i][K2]>Tree[R][K2])//再看看它K2值符不符合最小堆的要求
{
result=0;
break;
}
}
else if(L==-1&&R!=-1)//如果该节点左子树空而右子树不空
{
Min=GetMin(Tree,R);//获得该节点右子树的最小值
if(Tree[i][K1]>Tree[R][K1]||Tree[i][K2]>Tree[R][K2]||Tree[i][K2]>Min)//看看它同时符不符合搜索树和最小堆的要求
{
result=0;
break;
}
}
else if(R==-1&&L!=-1)//如果该节点右子树空而左子树不空
{
Max=GetMax(Tree,L);//获得该节点左子树的最大值
if(Tree[i][K1]<Tree[L][K1]||Tree[i][K2]>Tree[L][K2]||Max>Tree[i][K1])//看看它同时符不符合搜索树和最小堆的要求
{
result=0;
break;
}
}
}
return result;//返回判断结果
}
/*获得树的最大值*/
int GetMax(int**Tree,int LT)
{
int Max;
while(Tree[LT][3]!=-1)//若右子树不为空,一直往右边走
LT=Tree[LT][3];
Max=Tree[LT][0];//取出最大值并返回
return Max;
}
/*获得树的最大值*/
int GetMin(int**Tree,int RT)
{
int Min;
while(Tree[RT][2]!=-1)//若左子树不为空,一直往左走
RT=Tree[RT][2];
Min=Tree[RT][1];//取出最小值并返回
return Min;
}
我的代码里有几段看得很不舒服(ŎдŎ;)
但我现在还没有想到该如何优化他,
完了,又是失眠夜。。。。(눈‸눈)