十九、二叉树的最近的公共祖先
题目描述
设顺序存储的二叉树中有编号为 i 和 j 的两个结点,请设计算法求出它们最近的公共祖先结点的编号和值。
输入:
输入第1行给出正整数n(≤1000),即顺序存储的最大容量;
第2行给出n个非负整数,其间以空格分隔。其中0代表二叉树中的空结点(如果第1个结点为0,则代表一棵空树);
第3行给出一对结点编号 i 和 j 。
题目保证输入正确对应一棵二叉树,且1≤ i,j ≤n。
输出:
如果i或j对应的是空结点,则输出:ERROR: T[x] is NULL,其中 x 是 i 或 j 中先发现错误的那个编号;否则在一行中输出编号为 i 和 j 的两个结点最近的公共祖先结点的编号和值,其间以一个空格分隔。
测试输入 | 期待的输出 | 时间限制 | 内存限制 | 额外进程 | |
---|---|---|---|---|---|
测试用例 1 | 15 4 3 5 1 10 0 7 0 2 0 9 0 0 6 8 11 4 | 2 3 | 1秒 | 64M | 0 |
测试用例 2 | 15 4 3 5 1 0 0 7 0 2 0 0 0 0 6 8 12 8 | ERROR: T[12] is NULL | 1秒 | 64M | 0 |
测试用例 3 | 15 4 3 5 1 0 0 7 0 2 0 0 0 0 6 8 8 12 | ERROR: T[8] is NULL | 1秒 | 64M | 0 |
解题思路
求解结点的最近公共祖先(Least Common Ancestors,LCA),做法如下:
从根节点开始遍历
- 如果node1和node2中的任一个和root匹配,那么root就是最低公共祖先。
- 如果都不匹配,则分别递归左、右子树,
- 如果有一个节点出现在左子树,并且另一个节点出现在右子树,则root就是最低公共祖先.
- 如果两个节点都出现在左子树,则说明最低公共祖先在左子树中,否则在右子树。
注意,如果 i、j 是空节点,直接输出 ERROR。
上机代码:
在二叉树的存储结构中,data是输入序列中二叉树结点的值,vis是按照完全二叉树的顺序对应二叉树的结点的编号。通过 vis 可以更简单地判断二叉树向下查找时有没有超出边界,同时通过打印 vis 可以知道二叉树有没有正确地建立。
下面代码中的两个函数 in_sequence()
、print()
,通过中序遍历打印 data 和 vis 的值,我借此来判断二叉树有没有正确建立,与本题关系不大。
#include<cstdio>
#include<iostream>
#include<queue>
#include<cstring>
#include<cstdlib>
#include<algorithm>
using namespace std;
typedef struct NODE
{
int data; //结点的值
int vis; //结点的序号
struct NODE *lchild;
struct NODE *rchild;
}node, *Tree;
queue<Tree>q;
int n = 0, a[1010];//数组存储结点信息
void createTree()
{
int flag = 0;
Tree T, N;
T = (Tree)malloc(sizeof(node));
cin >> n;
for (int i = 1; i <= n; i++)
cin >> a[i];
//建立二叉树
while (!q.empty())
{
flag++;
T = q.front();
q.pop();
T->data = a[flag];
T->vis = flag;
N = (Tree)malloc(sizeof(node));
N->data = -1;
N->vis = -1;//没有访问过
T->lchild = N;
q.push(N);
N = (Tree)malloc(sizeof(node));
N->data = -1;
N->vis = -1;
T->rchild = N;
q.push(N);
if (flag == n)
break;
}
}
void in_sequence(Tree T)
{
if (T->data != -1)
{
in_sequence(T->lchild);
cout << T->data << " ";
in_sequence(T->rchild);
}
return;
}
void print(Tree T)
{
if (T->data != -1)
{
print(T->lchild);
cout << T->vis << " ";
print(T->rchild);
}
return;
}
Tree LCA(Tree T,int u,int v)
{
if (T->vis == -1)//超出搜索范围
return T;
if (T->vis == u || T->vis == v)//找到u,v的值
return T;
Tree cur = (Tree)malloc(sizeof(node));
//递归左右子树
Tree left_lca = (Tree)malloc(sizeof(node));
left_lca = LCA(T->lchild, u, v);
if (left_lca->vis != -1)
{
cur = LCA(left_lca->lchild, u, v);
if (cur->vis != -1)
cur = LCA(left_lca->rchild, u, v);
if ((cur->vis == u && left_lca->vis == v) || (cur->vis == v && left_lca->vis == u))
return T;
}
Tree right_lca = (Tree)malloc(sizeof(node));
right_lca = LCA(T->rchild, u, v);
if (right_lca->vis != -1)
{
cur = LCA(right_lca->lchild, u, v);
if (cur->vis != -1)
cur = LCA(right_lca->rchild, u, v);
if ((cur->vis == u && right_lca->vis == v) || (cur->vis == v && right_lca->vis == u))
return T;
}
if (left_lca->vis != -1 && right_lca->vis != -1)
return T;
if (left_lca->vis == -1)
return right_lca;
else
return left_lca;
}
int main()
{
memset(a, -1, sizeof(a));
Tree bit;
bit = (Tree)malloc(sizeof(node));
q.push(bit);
createTree();
/*
in_sequence(bit);
printf("\n");
print(bit);
printf("\n");
*/
int u = 0, v = 0;
cin >> u >> v;
if (a[u]==0)
{
printf("ERROR: T[%d] is NULL\n", u);
//system("pause");
return 0;
}
else if (a[v]==0)
{
printf("ERROR: T[%d] is NULL\n", v);
//system("pause");
return 0;
}
else
{
Tree tmp = (Tree)malloc(sizeof(node));
tmp=LCA(bit, u, v);
printf("%d %d\n", tmp->vis, tmp->data);
}
//system("pause");
return 0;
}