题目描述
知识点
树的遍历
实现
码前思考
- 这道题和之前的某道题很像,但是那道题是BST;
- 由于我是一个不会变通的人,所以我就把这个二叉树变成了BST,😓。根据中序遍历,对结点值重新编号。。。之后操作就跟原来那道题一样了。。。
代码实现
#include "bits/stdc++.h"
using namespace std;
const int maxn = 1e4+10;
//将二叉树映射成BST
unordered_map<int,int> mp;
//将BST映射为二叉树
int rev[maxn];
int pre[maxn];
int m;
int n;
int main(){
scanf("%d %d",&m,&n);
for(int i=1;i<=n;i++){
int key;
scanf("%d",&key);
mp[key] = i;
rev[i] = key;
}
//下面是先序遍历
for(int i=0;i<n;i++){
int key;
scanf("%d",&key);
pre[i] = mp[key];
}
//接下来是判断
for(int i=0;i<m;i++){
int u;
int v;
scanf("%d %d",&u,&v);
if(mp.count(u) == 0 && mp.count(v) == 0){
printf("ERROR: %d and %d are not found.\n",u,v);
}else if(mp.count(u) != 0 && mp.count(v) == 0){
printf("ERROR: %d is not found.\n",v);
}else if(mp.count(u) == 0 && mp.count(v) != 0){
printf("ERROR: %d is not found.\n",u);
}else{
//就去遍历
int mpu = mp[u];
int mpv = mp[v];
int father;
for(int i=0;i<n;i++){
if(pre[i] >= mpu && pre[i] <= mpv || pre[i] <= mpu && pre[i] >= mpv ){
//出现了这种分叉的情况
father = pre[i];
break;
}
}
//映射回去
int root = rev[father];
if(root == u){
printf("%d is an ancestor of %d.\n",u,v);
}else if(root == v){
printf("%d is an ancestor of %d.\n",v,u);
}else{
printf("LCA of %d and %d is %d.\n",u,v,root);
}
}
}
return 0;
}
码后反思
-
我还是太菜了,柳神是利用中序和前序进行遍历得到结果的:
#include <iostream> #include <vector> #include <map> using namespace std; map<int, int> pos; vector<int> in, pre; void lca(int inl, int inr, int preRoot, int a, int b) { if (inl > inr) return; int inRoot = pos[pre[preRoot]], aIn = pos[a], bIn = pos[b]; if (aIn < inRoot && bIn < inRoot) lca(inl, inRoot-1, preRoot+1, a, b); else if ((aIn < inRoot && bIn > inRoot) || (aIn > inRoot && bIn < inRoot)) printf("LCA of %d and %d is %d.\n", a, b, in[inRoot]); else if (aIn > inRoot && bIn > inRoot) lca(inRoot+1, inr, preRoot+1+(inRoot-inl), a, b); else if (aIn == inRoot) printf("%d is an ancestor of %d.\n", a, b); else if (bIn == inRoot) printf("%d is an ancestor of %d.\n", b, a); } int main() { int m, n, a, b; scanf("%d %d", &m, &n); in.resize(n + 1), pre.resize(n + 1); for (int i = 1; i <= n; i++) { scanf("%d", &in[i]); pos[in[i]] = i; } for (int i = 1; i <= n; i++) scanf("%d", &pre[i]); for (int i = 0; i < m; i++) { scanf("%d %d", &a, &b); if (pos[a] == 0 && pos[b] == 0) printf("ERROR: %d and %d are not found.\n", a, b); else if (pos[a] == 0 || pos[b] == 0) printf("ERROR: %d is not found.\n", pos[a] == 0 ? a : b); else lca(1, n, 1, a, b); } return 0; }
需要存储数字所在的位置;
-
我发现了原来PAT这种根据先,中,后序列来做题的题,一般都不用建树!!!
二刷代码
学会了不用建树的代码,其实我们想要的只是遍历,那么根本不用建树,因为序列已经能够帮助我们遍历了。本题的关键就是在于寻找第一个使得两者不在同一棵树上的结点:
//建树的过程相当于先序遍历,那么我们可以看在哪一步对其进行了分割
#include <iostream>
#include <unordered_map>
#include <algorithm>
using namespace std;
const int maxn = 1010;
unordered_map<int,int> mp;
int in[maxn];
int pre[maxn];
int u,v;
int pos;
bool flag=false;
void create(int preL,int preR,int inL,int inR){
if(flag){
return;
}
if(preL>preR){
return;//其实这句话应该是没用的
}
int root = pre[preL];
int k;
for(k=inL;k<=inR;k++){
if(root==in[k]){
break;
}
}
//那么找到了k的方位
if(!((mp[u]>k&&mp[v]>k)||mp[u]<k&&mp[v]<k)){//说明这时不在一棵树上了
pos=k;
flag=true;
return;
}else{
//继续递归
int numLeft = k-inL;
create(preL+1,preL+numLeft,inL,k-1);
create(preL+numLeft+1,preR,k+1,inR);
}
return;
}
int main(){
int m,n;
scanf("%d %d",&m,&n);
for(int i=0;i<n;i++){
scanf("%d",&in[i]);
mp[in[i]] = i;
}
for(int i=0;i<n;i++){
scanf("%d",&pre[i]);
}
for(int i=0;i<m;i++){
flag=false;
pos=-1;
scanf("%d %d",&u,&v);
if(mp.count(u)==0 && mp.count(v)==0){
printf("ERROR: %d and %d are not found.\n",u,v);
}else if(mp.count(u)==0){
printf("ERROR: %d is not found.\n",u);
}else if(mp.count(v)==0){
printf("ERROR: %d is not found.\n",v);
}else{
//说明都存在在树上
create(0,n-1,0,n-1);
printf("%d %d\n",pos,in[pos]);
if(in[pos]==u){
printf("%d is an ancestor of %d.\n",u,v);
}else if(in[pos]==v){
printf("%d is an ancestor of %d.\n",v,u);
}else{
printf("LCA of %d and %d is %d.\n",u,v,in[pos]);
}
}
}
return 0;
}