笛卡尔树是一种特殊的二叉树,其结点包含两个关键字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
法1
提交结果
提交时间 | ״̬ | 分数 | 题目 | 编译器 | 耗时 | 用户 |
---|---|---|---|---|---|---|
2018/5/24 21:25:05 | 答案正确 | 20 | 7-4 | C++ (g++) | 17GJ54 |
测试点 | 提示 | 结果 | 耗时 | 内存 |
---|---|---|---|---|
0 | sample 1 | 答案正确 | 128KB | |
1 | sample 2 | 答案正确 | 128KB | |
2 | K1满足二叉搜索树,但K2不满足最小堆顺序 | 答案正确 | 128KB | |
3 | K2满足最小堆顺序,但K1不满足二叉搜索树 | 答案正确 | 128KB | |
4 | K2满足最小堆顺序;K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件 | 答案正确 | 128KB | |
5 | 答案正确 | 128KB | ||
6 | 答案正确 | 128KB |
其中在找根节点的时候利用vis数组,将有父亲节点的孩子标记,则最后那个没有标记(没有父亲的)就是根节点了。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<algorithm> using namespace std; struct node { int left; int right; int k1,k2; } q[1010]; int flag; int vis[1010]; void puandui(int num) { int left,right; if(q[num].left!=-1) { left = q[num].left; if(q[left].k2<q[num].k2)//如果左孩子的K2比父亲的K2小则不符合最小堆 { flag = 0; return ; } puandui(left);//递归 } if(q[num].right !=-1) { right = q[num].right; if(q[right].k2<q[num].k2)//如果右孩子的K2比父亲的K2小则不符合最小堆 { flag = 0; return ; } puandui(right); } } int zhongxu1[1010],zhongxu2[1010]; int cnt; void zhongxu(int num) { if(num!=-1) { zhongxu(q[num].left); zhongxu1[cnt] = q[num].k1; zhongxu2[cnt] = q[num].k1; cnt++; zhongxu(q[num].right); } } int main() { int n; scanf("%d",&n); cnt = 0; flag = 1; int l,r,k1,k2; memset(vis,0,sizeof(vis)); for(int i=0; i<n; i++) { scanf("%d %d %d %d",&k1,&k2,&l,&r); q[i].k1 = k1; q[i].k2 = k2; q[i].left = l; q[i].right = r; if(l!=-1)//有父亲的左右孩子标记 vis[l] = 1; if(r!=-1) vis[r] = 1; } int root; for(int i=0; i<n; i++) { if(vis[i]==0)//找根节点 { root = i; break; } } puandui(root);//判断k2是否符合最小堆 zhongxu(root);//中序遍历。 sort(zhongxu1,zhongxu1+cnt);//从小到大排序 for(int i=0; i<cnt; i++) { if(zhongxu1[i]!=zhongxu2[i]) { flag = 0; break; } } if(flag) printf("YES\n"); else printf("NO\n"); return 0;}
法2
提交结果
提交时间 | ״̬ | 分数 | 题目 | 编译器 | 耗时 | 用户 |
---|---|---|---|---|---|---|
2018/5/24 21:23:41 | 答案正确 | 20 | 7-4 | C (gcc) | 17GJ54 |
测试点 | 提示 | 结果 | 耗时 | 内存 |
---|---|---|---|---|
0 | sample 1 | 答案正确 | 128KB | |
1 | sample 2 | 答案正确 | 128KB | |
2 | K1满足二叉搜索树,但K2不满足最小堆顺序 | 答案正确 | 128KB | |
3 | K2满足最小堆顺序,但K1不满足二叉搜索树 | 答案正确 | 128KB | |
4 | K2满足最小堆顺序;K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件 | 答案正确 | 128KB | |
5 | 答案正确 | 128KB | ||
6 | 答案正确 | 128KB |
无非就是用两种遍历方式分别验证每个结点的K1值和K2值是否分别符合规则。
#include <stdio.h> #include<stdlib.h> #define ERRORMin 99999//'这个地方其实是有一定风险,因为题目并没有指出K值的上界' #define ERRORMax -99999//同上 typedef struct node *Node; struct node { int K1; int Max_K1,Min_K1; int K2; int Left; int Right; }*Decare; int n; int Root(); int DLR(int);//前序,Degree Left Right int LRD(int);//后序,Left Right Degree int main() { scanf("%d",&n); Decare=(Node)malloc(sizeof(struct node)*n); for(int i=0; i<n; i++) { scanf("%d",&Decare[i].K1); scanf("%d",&Decare[i].K2); scanf("%d",&Decare[i].Left); scanf("%d",&Decare[i].Right); Decare[i].Min_K1=ERRORMin; Decare[i].Max_K1=ERRORMax; } int head=Root(); int Result =DLR(head); if(Result)Result=LRD(head); if(Result)printf("YES\n"); else printf("NO\n"); return 0; } int Root() { int*temp=(int*)malloc(sizeof(int)*n); for(int i=0; i<n; i++) { temp[i]=0; } for(int i=0; i<n; i++) { temp[Decare[i].Left]=1; temp[Decare[i].Right]=1; } for(int i=0; i<n; i++) { if(!temp[i])return i; } return -1;//没有根节点 } int DLR(int K) { //K2判定 if(K==-1)return 1; if(Decare[K].Left!=-1) if(Decare[Decare[K].Left].K2<Decare[K].K2)return 0; if(Decare[K].Right!=-1) if(Decare[Decare[K].Right].K2<Decare[K].K2)return 0; if( DLR(Decare[K].Left)==0)return 0; if(DLR(Decare[K].Right)==0)return 0; return 1; } int LRD(int K) {//K1判定,先把Min_K1 Max_K1计算出来在比较 if(K==-1)return 1; if(LRD(Decare[K].Left)==0)return 0; if(Decare[K].Left==-1)Decare[K].Min_K1=Decare[K].K1; else Decare[K].Min_K1=Decare[Decare[K].Left].Min_K1; if(LRD(Decare[K].Right)==0)return 0; if(Decare[K].Right==-1)Decare[K].Max_K1=Decare[K].K1; else Decare[K].Max_K1=Decare[Decare[K].Right].Max_K1; int temp=K; if(Decare[temp].Left!=-1) if(Decare[temp].K1<Decare[Decare[temp].Left].Max_K1)return 0; if(Decare[temp].Right!=-1) if(Decare[temp].K1>Decare[Decare[temp].Right].Min_K1)return 0; return 1; }
法3
提交结果
提交时间 | ״̬ | 分数 | 题目 | 编译器 | 耗时 | 用户 |
---|---|---|---|---|---|---|
2018/5/24 21:27:10 | 答案正确 | 20 | 7-4 | C++ (g++) | 17GJ54 |
测试点 | 提示 | 结果 | 耗时 | 内存 |
---|---|---|---|---|
0 | sample 1 | 答案正确 | 128KB | |
1 | sample 2 | 答案正确 | 128KB | |
2 | K1满足二叉搜索树,但K2不满足最小堆顺序 | 答案正确 | 128KB | |
3 | K2满足最小堆顺序,但K1不满足二叉搜索树 | 答案正确 | 128KB | |
4 | K2满足最小堆顺序;K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件 | 答案正确 | 128KB | |
5 | 答案正确 | 128KB | ||
6 | 答案正确 | 128KB |
BST的满足键值判断,要依据中序遍历是否有序
#include<stdio.h> #include<string.h> #include<stdlib.h> int pre[1010]; struct BST{ int left; int right; int k1; int k2; }q[1010]; int flag; void Judge(int num) { int left,right; if(!flag) return; if(q[num].left!=-1) { left=q[num].left; if(q[left].k2<q[num].k2) { flag=0; return; } Judge(left); } if(q[num].right!=-1) { right=q[num].right; if(q[right].k2<q[num].k2) { flag=0; return; } Judge(right); } } int bb[1010],aa[1010]; int num_b; void inorder(int num) { if(num==-1) return; inorder(q[num].left); aa[num_b]=bb[num_b]=q[num].k1; num_b++; inorder(q[num].right); } int main() { int n,k1,k2,left,right; memset(pre,0,sizeof(pre)); scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%d%d%d%d",&k1,&k2,&left,&right); q[i].k1=k1; q[i].k2=k2; q[i].left=left; q[i].right=right; if(left!=-1) pre[left]++; if(right!=-1) pre[right]++; } int root; for(int i=0;i<n;i++) { if(!pre[i]) { root=i; break; } } flag=1; Judge(root); num_b=0; inorder(root); for(int i=0;i<num_b;i++) { for(int j=i+1;j<num_b;j++) { if(aa[j]<aa[j-1]) { int temp=aa[j-1]; aa[j-1]=aa[j]; aa[j]=temp; } } } for(int i=0;i<num_b;i++) { if(aa[i]!=bb[i]) { flag=0; break; } } if(flag) puts("YES"); else puts("NO"); return 0; }
法4
提交结果
提交时间 | ״̬ | 分数 | 题目 | 编译器 | 耗时 | 用户 |
---|---|---|---|---|---|---|
2018/5/24 21:26:28 | 答案正确 | 20 | 7-4 | C++ (g++) | 17GJ54 |
测试点 | 提示 | 结果 | 耗时 | 内存 |
---|---|---|---|---|
0 | sample 1 | 答案正确 | 240KB | |
1 | sample 2 | 答案正确 | 236KB | |
2 | K1满足二叉搜索树,但K2不满足最小堆顺序 | 答案正确 | 236KB | |
3 | K2满足最小堆顺序,但K1不满足二叉搜索树 | 答案正确 | 236KB | |
4 | K2满足最小堆顺序;K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件 | 答案正确 | 240KB | |
5 | 答案正确 | 236KB | ||
6 | 答案正确 | 240KB |
样例输入与输出:
序号 | 输入 | 输出 |
1 | YES | |
2 | NO | |
3 | 7 8 27 5 1 9 40 -1 -1 10 20 0 3 12 22 -1 4 15 21 6 -1 5 35 -1 -1 13 23 -1 -1 | NO |
4 | NO | |
5 | 9 11 5 3 -1 15 3 4 7 5 2 6 0 6 8 -1 -1 9 6 -1 8 10 1 2 1 2 4 -1 -1 20 7 -1 -1 12 9 -1 -1 | NO |
6 | 1 1 1 -1 -1 | YES |
二叉排序树的中序遍历一定是一个从小到大排序的数组,这题我在判断是否是二叉树的时候分了2步, 先得到中序的数组然后来进行判断。方法有点笨。
另外题目给的测试数据有问题,有几处-1的负号不是同一种类型的。大家注意下,自己改回来
#include <iostream> #include <deque> using namespace std; typedef struct { int K1; int K2; int lChild; int rChild; }BNode; deque<BNode> de; deque<int> tde; bool a[1005] = {false}; int sum = 0; void PreTraval(BNode root) { if(root.lChild != -1) PreTraval(de[root.lChild]); tde.push_back(root.K1); if(root.rChild != -1) PreTraval(de[root.rChild]); } bool IsSDD(BNode root) { if(root.lChild == -1 && root.rChild == -1) return true; else if(root.lChild == -1 && root.rChild != -1) { if(de[root.rChild].K2 > root.K2) return IsSDD(de[root.rChild]); else return false; } else if(root.lChild != -1 && root.rChild == -1) { if(de[root.lChild].K2 > root.K2) return IsSDD(de[root.lChild]); else return false; } else { if(root.K2 < de[root.lChild].K2 && root.K2 < de[root.rChild].K2) return IsSDD(de[root.lChild]) && IsSDD(de[root.rChild]); else return false; } } bool IsBST() { for(int i = 1; i < tde.size(); ++i) { if(tde[i] <= tde[i-1]) return false; } return true; } int main() { int n; cin>>n; while(n--) { BNode tn; scanf("%d%d%d%d", &tn.K1, &tn.K2, &tn.lChild, &tn.rChild); // cin>>tn.K1>>tn.K2>>tn.lChild>>tn.rChild; if(tn.lChild >= 0) a[tn.lChild] = true; if(tn.rChild >= 0) a[tn.rChild] = true; de.push_back(tn); } int rootIndex = 0; for(int i = 0; i < de.size(); ++i) if(!a[i]) { rootIndex = i; break; } PreTraval(de[rootIndex]); if(IsBST() && IsSDD(de[rootIndex])) cout<<"YES"<<endl; else cout<<"NO"<<endl; return 0; }
法5
提交结果
提交时间 | ״̬ | 分数 | 题目 | 编译器 | 耗时 | 用户 |
---|---|---|---|---|---|---|
2018/5/24 21:28:25 | 答案正确 | 20 | 7-4 | C++ (g++) | 17GJ54 |
测试点 | 提示 | 结果 | 耗时 | 内存 |
---|---|---|---|---|
0 | sample 1 | 答案正确 | 240KB | |
1 | sample 2 | 答案正确 | 240KB | |
2 | K1满足二叉搜索树,但K2不满足最小堆顺序 | 答案正确 | 240KB | |
3 | K2满足最小堆顺序,但K1不满足二叉搜索树 | 答案正确 | 240KB | |
4 | K2满足最小堆顺序;K1的每个子树都满足二叉搜索树条件,但整棵树不满足二叉搜索树条件 | 答案正确 | 312KB | |
5 | 答案正确 | 240KB | ||
6 | 答案正确 | 240KB |
笛卡尔树的中序遍历应为一升序的序列!
#include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #include <vector> using namespace std; const int maxn = 1017; int tag[maxn]; typedef struct { int k1; int k2; int L; int R; }Node; vector<int>a; vector<Node>b; void Mid_order(Node T)//中序遍历 { if(T.L != -1) { Mid_order(b[T.L]); } a.push_back(T.k1); if(T.R != -1) { Mid_order(b[T.R]); } } int is_dkr(Node T) { if(T.L==-1 && T.R==-1) return 1; else if(T.L!=-1 && T.R==-1) { if(b[T.L].k2 > T.k2) return is_dkr(b[T.L]); else return 0; } else if(T.L==-1 && T.R!=-1) { if(b[T.R].k2 > T.k2) return is_dkr(b[T.R]); else return 0; } else if(T.L!=-1 && T.R!=-1) { if(b[T.L].k2 > T.k2 && b[T.R].k2 > T.k2) return is_dkr(b[T.L]) && is_dkr(b[T.R]); else return 0; } } int main() { int n; while(~scanf("%d",&n)) { int i, j; memset(tag,0,sizeof(tag)); a.clear(); b.clear(); Node c; for(i = 0; i < n; i++) { scanf("%d%d%d%d",&c.k1,&c.k2,&c.L,&c.R); if(c.L >= 0) { tag[c.L] = 1; } if(c.R >= 0) { tag[c.R] = 1; } b.push_back(c); } int root_node = 0; for(i = 0; i < b.size(); i++) { if(!tag[i]) { root_node = i;//根节点 break; } } Mid_order(b[root_node]); int flag = 0; for(i = 1; i < a.size(); i++) { if(a[i] <= a[i-1]) { flag = 1; break; } } if(flag) { printf("NO\n"); continue; } if(!is_dkr(b[root_node])) { printf("NO\n"); } else { printf("YES\n"); } } return 0; }
文章来源: 笛卡尔树