5.1 笛卡尔树
分数 25 作者 DS课程组 单位 浙江大学
题目描述
笛卡尔树是一种特殊的二叉树,其结点包含两个关键字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
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
题目分析
对于要求的笛卡尔树满足:
- 中序遍历,结点k1值依次增大
- 结点k2值小于左右子孩子结点的k1值(类似层序)
根据输入样例,采用结构体数组的方式储存树,在题目输入的四项数据的基础上,添加parent父母结点编号,方便找到根节点(根节点parent=-1)
struct Node {
int k1, k2, lc, rc, parent = -1;
}t[1000];
接着使用递归方法进行中序遍历分析
- 创建root指针,初始指向根结点;
- 中序遍历同时可以比较当前root指向结点与其左右结点的k2值;
- 设立tag标志,tag==1时树不符合要求
- 如果子节点k2值<root指向结点k2值,tag变为1,退出中序遍历递归函数
- 将中序遍历k1值储存在队列M中,后续进行检验M中数据是否递增
void zhongxv(Node* root) {
Node* left, * right;
left = &t[root->lc];
right = &t[root->rc];
if (root->lc != -1) { //左子树存在,递归遍历左子树
if (root->k2 > left->k2) {//如果k2不满足要求,立即跳出进行输出
tag = 1;
return;
}
zhongxv(left);
}
M.push(root->k1); //按中序遍历将k1值入队
if (root->rc != -1) { //右子树存在,递归遍历右子树
if (root->k2 > right->k2) {
tag = 1;
return;
}
zhongxv(right);
}
}
完整代码
#include<iostream>
#include<queue>
using namespace std;
struct Node {
int k1, k2, lc, rc, parent = -1;
}t[1000];
Node* root;
queue<int>M;
int n;
int tag = 0;
void zhongxv(Node* root) {
Node* left, * right;
left = &t[root->lc];
right = &t[root->rc];
if (root->lc != -1) {
if (root->k2 > left->k2) {
tag = 1;
return;
}
zhongxv(left);
}
M.push(root->k1);
if (root->rc != -1) {
if (root->k2 > right->k2) {
tag = 1;
return;
}
zhongxv(right);
}
}
int main() {
cin >> n;
for (int i = 0; i < n; i++) {
cin >> t[i].k1 >> t[i].k2 >> t[i].lc >> t[i].rc;
if (t[i].lc != -1)
t[t[i].lc].parent = i;
if (t[i].rc != -1)
t[t[i].rc].parent = i;
}
for (int i = 0; i < n; i++) {
if (t[i].parent == -1) {
root = &t[i];
break;
}
}
zhongxv(root);
int tmp=M.front();
M.pop();
while (!M.empty()) {
if (tmp > M.front()) {
tag = 1;
break;
}
tmp = M.front();
M.pop();
}
if (tag == 1)
cout << "NO";
else cout << "YES";
return 0;
}
过程中遇到的问题&总结
- 指针掌握不够扎实。(或许本题也可以不用指针,直接用使用index,但既然先想到了指针,写完程序一片
红色下标
) - 链表是个好东西,但搓不顺,有些时候还是可以用用数组什么的,更快一点,也不容易出错
- 第一次用
<queue>
库,好评,妈妈再也不用害怕我写不出队列了呢
特别鸣谢帆姐用买饭间隙帮我解答指针问题(bushi)