一:题目
7-31 笛卡尔树 (25 分)
笛卡尔树是一种特殊的二叉树,其结点包含两个关键字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
二:题目分析+思路
分析题意和示例:
每一行代表这个结点的序号(即第几行为第几个结点)
拿示例一来说:(其实也就是 该结点 该结点的左孩子,该结点的右孩子)
6
(0) 8 27 5 1
(1) 9 40 -1 -1
(2) 10 20 0 3
(3) 12 21 -1 4
(4) 15 22 -1 -1
(5) 5 35 -1 -1
思路:
用 两个map<int,int> 进行 存前两行的数据 其中 第一个int为行号,第二个int
为每行的数据,然后单独处理后两行进行建树,建完树后中序输出,然后通过map找到对应的
键值是否为升序,这是判读K1的条件是否成立;
笛卡尔树的第二个条件是 该树的除叶节点外,其余结点的值都比左右孩子的值大 ,这个既然树已经建好了
那就用递归做。
补充:其中建树的算法还很炫!代码中也有解释。
三:如果map容器的用法和vector容器的算法不熟悉 下方链接学习下哈
四:上码
/**
分析题意和示例:
每一行代表这个结点的序号(即第几行为第几个结点)
拿示例一来说:(其实也就是 该结点 该结点的左孩子,该结点的右孩子)
6
(0) 8 27 5 1
(1) 9 40 -1 -1
(2) 10 20 0 3
(3) 12 21 -1 4
(4) 15 22 -1 -1
(5) 5 35 -1 -1
思路:
先建树,用 两个map<int,int> 进行 存前两行的数据 其中 第一个int为 行号第二个int
为每行的数据,然后单独处理后两行进行建树,建完树后中序输出,然后通过map找到对应的
键值是否为升序,
是笛卡尔树的第二个条件是 该树的除叶节点外,其余结点的值都比左右孩子的值大
*/
#include<bits/stdc++.h>
using namespace std;
typedef struct TNode* ptrTree;
typedef struct TNode{
int data;
ptrTree left;
ptrTree right;
}tnode;
int cnt;//记录递归次数
vector<int>v; //存中序遍历的序号
map<int,int>m1,m2;//存前两行的数据
int flag2 = 0;//用于判断K2
ptrTree createTree(int number[1000][2],int x){
if(x == -1)
return NULL;
cnt++;
ptrTree BT = (ptrTree)malloc(sizeof(struct TNode));
BT->left = NULL;
BT->right = NULL;
BT->data = x;
// cout << BT->data << endl;
BT->left = createTree(number,number[x][0]);
BT->right = createTree(number,number[x][1]);
return BT;
}
//中序遍历
void inorder(ptrTree BT){
if( BT ){
inorder(BT->left);
int temp = BT->data;
v.push_back(temp);
inorder(BT->right);
}
}
//是笛卡尔树的第二个判断条件
void judgement(ptrTree BT){
if( BT->left != NULL ){
if(m2[BT->data] > m2[BT->left->data]){
flag2 = 1;
return ;
}
judgement(BT->left);
}
if( BT->right != NULL ){
if(m2[BT->data] > m2[BT->right->data]){
flag2 = 1;
return ;
}
judgement(BT->right);
}
}
int main(){
int N,flag1 = 0;
int array[1000][2];//定义二维数组存储后两行的数据
int m;//记录根节点用的
cin >> N;
for( int i = 0; i < N; i++ )
{
int a,b,c,d;
cin >> a >> b >> c >> d;
m1[i] = a; m2[i] = b;//存储前两行数据
array[i][0] = c; array[i][1] = d;//存储后两行数据
}
for( int i = 0; i < N; i++)
{
//建树
cnt = 0;
ptrTree BT;
BT= createTree(array,i); //这种建树方法 其实指定根节点最好 但如果没有的话
//那就挨个进行建树 如果其递归次数达到结点数目,那么就建树正确
if(cnt == N){
inorder(BT);//将中序遍历的顺序存入 vector中
judgement(BT);//条件K2
break;
}
}
if( flag2 == 1)
cout << "NO";
else{
for(int i = 0; i < v.size()-1; i++)
{
if( m1[v[i]] > m1[v[i+1]])
{
cout << "NO";
flag1 = 1;
break;
}
}
}
if(flag1 == 0 && flag2 == 0 )
cout << "YES";
}
//测试测试点三 即K2的判断
//6
//8 27 5 1
//9 25 -1 -1
//10 20 0 3
//12 21 -1 4
//15 22 -1 -1
//5 35 -1 -1
四:总结
我在分析题意时 以为 K2的判断条件为 只要根节点的关键值最小就OK,其实不然应该是所有非叶结点的值都大于其左右孩子,这样导致测试点三一直过不去,然后我就重新分析题意,发现自己的逻辑错误,然后就找样例就行验证自己猜想,结果验证正确。我想说的是啥呢,就是在做题当中即便有一个点不过,也不要放弃,继续死磕,回归题目是根本,重新梳理逻辑。加油BOY!