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

求树中的两个节点的最低公共祖先,是一组题目,不同条件下的题目是完全不一样的。

情形1、二叉搜索树

分析:如果树是二叉搜索树的话,就比较容易解决。因为二叉搜索树的特性,左子树的上节点的值比根节点小,右子树上节点的值比根节点大。

思路:从树的根节点开始和两个输入的节点进行比较。

(1)如果当前节点的值比两个节点的值都大,最低公共祖先结点一定在当前结点的左子树。

(2)如果当前节点的值比两个节点的值都小,最低公共祖先节点一定在当前结点的右子树。

(3)如果一个结点比当前结点大或等于,另一个比当前结点小或等于,则最低公共祖先节点为当前结点。

时间复杂度为O(lgN),空间复杂度为O(1)。

Node *GetLastCommomParent(Node *pRoot,Node* p1,Node* p2)
{
    if(pRoot == NULL || p1 == NULL || p2 == NULL)
        return NULL;
    //比根节点都小,到左子树中找
    if(p1->data < pRoot->data && p2->data <pRoot->data)
        return GetLastCommomParent(pRoot->left,p1,p2);
    //比根节点都大,到右子树中找
    else if(p1->data > pRoot->data && p2->data > pRoot->data)
        return GetLastCommomParent(pRoot->right,p1,p2);
    else //其他情况
        return pRoot;
}
情形2、普通树,且有指向父节点的指针

如果树中有指向父节点的指针,那么可以把两个节点到根节点的路径想象成两条链表,那么这个问题可以转化为两个链表的第一个公共节点。

这里写图片描述

举例:求出F和H的最低公共祖先
F->D->B->A
H->E->B->A
这个两个链表的第一个交点B为他们的最低公共祖先。

情形3、普通树,没有指向父节点的指针

方法1、
用两个链表分别保存从根节点到输入的两个节点的路径,然后把问题转换成两个链表的最后公共节点。

时间复杂度:为了得到从根节点开始到输入的两个节点的两条路径,需要遍历两次树,每遍历一次的时间复杂度为O(N),得到两条路径的长度最差的是O(N),通常情况下两条路径是O(lgN)。

注:下面的GetNodePath函数,并没有把最后的节点放入list中
F :A->B->D
H:A->B->E

//找节点的路径,保存到list中
//注:参数path是引用,因为path需要改变里面的内容,
bool GetNodePath(Node* pRoot,Node* pNode,list<Node*>& path)
{
    if(pRoot == NULL || pNode == NULL)
        return false;
    path.push(pRoot);
    if(pRoot == pNode)
        return true;

    bool found = false;
    /*
    //树的孩子放在一个vector中
    vector<Node*>::iterator iter = pRoot->child.begin();
    while(!found && iter  < pRoot->child.end())
    {
        found = GetNodePath(*iter, pNode,path);
        ++iter;
    }
    if(!found)
        path.pop_back();
    */
    //为方便测试,可以暂时将树特指为二叉树
    found = GetNodePath(pRoot->left,pNode,path);
    if(!found)
        found = GetNodePath(pRoot->right,pNode,path);
    //左右子树都找不到的话,该节点弹出
    if(!found)
        path.pop_back();
    return found;
}


//求两个链表的最后一个公共节点
Node *GetLastCommonNode(const list<Node*>& path1, const list<Node*>& path2)
{
    //从后往前找
    list<Node*>::const_iterator it1 = path1.end();
    list<Node*>::const_iterator it2 = path2.end();
    while(--it1 != path1.begin() && --it2 != path2.begin())
    {
        if(*it1 == *it2)
            return *it1;
    }
    return NULL;    
}

Node* GetLastCommonParent(Node* pRoot,Node* pNode1,Node* pNode2)
{
    list<Node*> path1;
    GetNodePath(pRoot,pNode1,path1);
    list<Node*> path2;
    GetNodePath(pRoot,pNode2,path2);

    return GetLastCommonNode(path1,path2);
}

方法2、
(1)如果两个节点分别在根节点的左子树和右子树,则返回根节点
(2)如果两个节点都在左子树,则递归处理左子树;如果两个节点都在右子树,则递归处理右子树。

//判断节点是否在树中
bool FindNode(Node *pRoot,Node *pNode)  
{  
    if (pRoot == NULL || pNode == NULL)  
        return false;  
    if (pRoot == pNode)  
        return true;  
    bool found = FindNode(pRoot->left,pNode);  
    if (!found)  
        return FindNode(pRoot->right,pNode);  
    return found;
} 

//
Node* GetLastCommomParent(Node* pRoot, Node* pNode1, BNode* pNode2)  
{  
    //判断节点pNode1是否在左子树中
    if (FindNode(pRoot->_pLeftChild, pNode1))  
    {  
        if (FindNode(pRoot->_pRightChild, pNode2))  
            return pRoot;  
        else //两个节点都在左子树中
            return GetLastCommomParent(pRoot->_pLeftChild,pNode1,pNode2);  
    }  
    else //说明pNode1在右子树中
    {  
        if (FindNode(pRoot->_pLeftChild, pNode2))  
            return pRoot;  
        else  //节点都在右子树中,
            return GetLastCommomParent(pRoot->_pRightChild,pNode1,pNode2);  
    }  
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值