一:题目
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容器的算法不熟悉 下方链接学习下哈
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!