二叉树最近的公共祖先

题目信息

设顺序存储的二叉树中有编号为 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结点了。
判定递归结束的条件便是找到了其中一个节点与其相等。
需要注意的是,当读取二叉树到叶尾时,需返回一个空指针来表示并未找到匹配的答案。

相关题目

二叉树的建立与基本操作
二叉树遍历序列还原
二叉树的同构

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhj12399

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值