【数据结构与算法(二十七)】

题目

树中两个节点的最低公共祖先

设计一个函数,输入两个树节点,求它们的最低公共祖先。树是最普通的树,不是二叉树。可以使用辅助内存

思路

1、使用两个链表,分别保存从根节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点。
2、这里写图片描述
3、用前序遍历的方法来得到从根节点到H的路径的过程:遍历A,把A存到路径中;遍历到B,把B存到路径中,此时路径为A->B;遍历到D,把D存到路径中,此时路径为A->B->D;遍历到F,把F存到路径中,此时路径为A->B->D->F;F已经是叶子节点了,所以这条路径不可能到达节点H,于是把F从路径中删除,从D又遍历到G,路径为A->B->D->G。结果和遍历到F节点一样,这条路径同样也不能到达节点H。从路径中删除节点G,由于D的所有子节点都遍历过了,不可能到达节点H,因此D不在从A到H的路径中,把节点D从路径中删除,变成A->B。接着由B节点遍历到E节点,把E存放到路径中,遍历H,已经到达目标节点【这个过程其实就是之前的一道“在树中找到达某一输入节点的路径”的类型题】
4、每找到一个节点的路径,就需要遍历一遍树,遍历一次的时间复杂度是O(n)。于是得到两条路径的长度在最差情况是O(n)

#include<list>
#include<vector>
using std::vector;
using std::list;

struct TreeNode
{
    int nValue;
    vector<TreeNode*> childNodes;
};

bool GetNodePath(TreeNode* pRoot, TreeNode* pNode, list<TreeNode*>& path)
{
    if (pRoot == pNode)
        return true;
    path.push_back(pRoot);
    bool found = false;
    vector<TreeNode*>::iterator i = pRoot->childNodes.begin();
    while (!found&&i < pRoot->childNodes.end())
    {
        found = GetNodePath(*i, pNode, path);
        ++i;
    }
    //如果不在这条路径中那就弹出下,之后返回,然后进入下一次遍历
    if (!found)
        path.pop_back();
    return found;
}

TreeNode* GetLastCommonNode(const list<TreeNode*>& path1, const list<TreeNode*>& path2)
{
    list<TreeNode*>::const_iterator it1 = path1.begin();
    list<TreeNode*>::const_iterator it2 = path2.begin();

    TreeNode* pLast = nullptr;
    while (it1 != path1.end() && it2 != path2.end())
    {
        if (*it1 == *it2)
            pLast = *it1;
        it1++;
        it2++;
    }
    return pLast;//返回链表中最后一个相同的节点
}

TreeNode* GetLastCommonParent(TreeNode* pRoot, TreeNode* pNode1, TreeNode* pNode2)
{
    if (pRoot == nullptr || pNode1 == nullptr || pNode2 == nullptr)
        return nullptr;
    list<TreeNode*> path1;
    GetNodePath(pRoot, pNode1, path1);

    list<TreeNode*> path2;
    GetNodePath(pRoot, pNode2, path2);

    return GetLastCommonNode(path1, path2);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值