给出一颗二叉树的后序遍历和中序遍历,你能计算出两个结点的最近公共祖先吗?
输入格式:
第一行给出两个整数N(N<=10000)和M(M<=10000),分别代表二叉树的结点数和我们接下来的询问数。
第二行和第三行分别给出N个整数,每个整数用空格分开,分别代表二叉树的后序遍历和中序遍历。
接下来M行,每行给出两个整数,代表我们要询问的两个结点的编号a和b。
输出格式:
对于每个我们要求的询问:
1.如果a和b中有一个或两个不在树上,输出"ERROR"。
2.否则在一行中输出一个整数,表示a和b的最近公共祖先。
输入样例:
在这里给出一组输入。例如:
3 3
2 3 1
2 1 3
1 2
2 3
0 3
输出样例:
在这里给出相应的输出。例如:
1
1
ERROR
题目相当于由后序遍历和中序遍历得到树的父子关系,这里采用层序遍历记录数组 path[i] = j ,表示顶点 i 的父亲是 j 。
#include <iostream>
#include <algorithm>
#include <queue>
#include <cstring>
using namespace std;
const int maxn = 1e4 + 7;
int back[maxn], mid[maxn]; //后序与中序
int path[maxn]; //记录路径
bool Vis[maxn]; //标记是否访问
struct node{
int L1, R1; //后序
int L2, R2; //中序
};
int GetPath(node T) //返回根节点在中序的下标
{
for (int i = T.L2; i <= T.R2; i++)
if(mid[i] == back[T.R1])
return i;
return 0;
}
int PrintPath(int a, int b) //返回a, b顶点的最近公共祖先
{
while (a)
Vis[a] = true, a = path[a];
while (b)
{
if(Vis[b])
return b;
b = path[b];
}
return 0;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
cin >> back[i];
for (int i = 1; i <= n; i++)
cin >> mid[i];
/*借助队列, 用层序遍历*/
queue<node> q;
q.push({1, n, 1, n});
while (!q.empty())
{
int k = GetPath(q.front());
// cout << k << endl;
int L1 = q.front().L1; //后序左
int R1 = q.front().R1; //后序右
int L2 = q.front().L2; //中序左
int R2 = q.front().R2; //中序右
q.pop();
if(L2 < k) //如果有左节点, 入队, 算出子树的根作为当前树的左孩子
q.push({L1, L1 + (k - L2) - 1, L2, k - 1}), path[mid[GetPath({L1, L1 + (k - L2) - 1, L2, k - 1})]] = mid[k];
if(k < R2) //如果有右节点, 入队, 算出子树的根作为当前树的右孩子
q.push({L1 + k - L2, R1 - 1, k + 1, R2}), path[mid[GetPath({L1 + k - L2, R1 - 1, k + 1, R2})]] = mid[k];
}
while (m--)
{
int a, b;
cin >> a >> b;
if(a < 1 || a > n || b < 1 || b > n)
{
cout << "ERROR" << endl;
continue;
}
memset(Vis, false, sizeof(Vis));
cout << PrintPath(a, b) << endl;
}
system("pause");
return 0;
}