题目信息
设顺序存储的二叉树中有编号为 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
测试样例2
15
4 3 5 1 0 0 7 0 2 0 0 0 0 6 8
12 8
ERROR: T[12] is NULL
测试样例3
15
4 3 5 1 0 0 7 0 2 0 0 0 0 6 8
8 12
ERROR: T[8] is NULL
解答
#include <iostream>
#include <queue>
#include <stack>
using namespace std;
struct BTreeNode
{
int data;
BTreeNode *Left;
BTreeNode *Right;
};
class BTree
{
public:
void ShunXuCunChuCreate(BTreeNode *Node, int *tmpchar, int N)
{//按顺序结构存储来构建
queue<BTreeNode *> que;
que.push(Node);
while (N--)
{
BTreeNode *node = que.front();
que.pop();
node->data = *tmpchar;
tmpchar++;
node->Left = new BTreeNode;
node->Left->data = '#';
que.push(node->Left);
node->Right = new BTreeNode;
node->Right->data = '#';
que.push(node->Right);
}
}
BTreeNode *Find(BTreeNode *Root, int x)
{//根据数值来寻找节点
if (Root->data == x)
{
return Root;
}
else if (Root->data == '#')
{
return NULL;
}
BTreeNode *left = Find(Root->Left, x);
if (left != NULL)
{
return left;
}
BTreeNode *right = Find(Root->Right, x);
if (right != NULL)
{
return right;
}
return NULL;
}
BTreeNode *lowestCommonAncestor(BTreeNode *root, BTreeNode *p, BTreeNode *q)
{//寻找最近公共祖先
if (root == p || root == q)
{//读到要找的节点
return root;
}
if (root->data == '#')
{//如果读到末尾节点了,则给回去一个NULL
return NULL;
}
BTreeNode *left = lowestCommonAncestor(root->Left, p, q);
BTreeNode *right = lowestCommonAncestor(root->Right, p, q);
if (!left && !right)
{//如果左右节点都没有,说明不在此节点下面
return NULL;
}
else if (left && !right)
{//如果仅亮左边,说明两个节点都在左边
return left;
}
else if (right && !left)
{
return right;
}
else
{//如果两边都亮的话,说明恰好在这两边
return root;//那么此时的节点就恰好是我们想求的节点
}
}
};
int main()
{
//freopen("/Users/zhj/Downloads/test.txt", "r", stdin);
int n;
cin >> n;
int tmpchar[1005];
for (int i = 0; i < n; i++)
{
cin >> tmpchar[i];
}
BTree bTree;
BTreeNode *Root = new BTreeNode;
bTree.ShunXuCunChuCreate(Root, tmpchar, n);
int x, y;
cin >> x >> y;
int xint = tmpchar[x - 1];
if (xint == 0)
{
cout << "ERROR: T[" << x << "] is NULL" << endl;
return 0;
}
int yint = tmpchar[y - 1];
if (yint == 0)
{
cout << "ERROR: T[" << y << "] is NULL" << endl;
return 0;
}
BTreeNode *xBtree = bTree.Find(Root, xint);
BTreeNode *yBtree = bTree.Find(Root, yint);
BTreeNode *Ans = bTree.lowestCommonAncestor(Root, xBtree, yBtree);
for (int i = 0; i < n; i++)
{
if (Ans->data == tmpchar[i])
{
cout << i + 1 << " " << Ans->data << endl;
return 0;
}
}
}
分析
第一种情况:左子树和右子树均找没有p结点或者q结点;(这里特别需要注意,虽然题目上说了p结点和q结点必定都存在,但是递归的时候必须把所有情况都考虑进去,因为题目给的条件是针对于整棵树,而递归会到局部,不一定都满足整体条件)
第二种情况:左子树上能找到,但是右子树上找不到,此时就应当直接返回左子树的查找结果;
第三种情况:右子树上能找到,但是左子树上找不到,此时就应当直接返回右子树的查找结果;
第四种情况:左右子树上均能找到,说明此时的p结点和q结点分居root结点两侧,此时就应当直接返回root结点了。
判定递归结束的条件便是找到了其中一个节点与其相等。
需要注意的是,当读取二叉树到叶尾时,需返回一个空指针来表示并未找到匹配的答案。