求二叉树中两个节点的最近公共祖先

本文探讨了三种不同情况下,在二叉树中寻找两个节点最近公共祖先的方法。针对搜索二叉树,可以通过节点位置判断;拥有parent指针的三叉链,可以借鉴链表求交点策略;对于普通二叉树,使用栈保存路径并利用链表思想找交点,以达到O(N)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

要求考虑以下三种种情况,给出解决方案,并解决:

1:二叉树是搜索二叉树。

2: 二叉树每个节点有parent(三叉链)

3:就是普通二叉树。(尽可能实现时间复杂度为O(N))
针对第一种情况,是一棵二叉搜索树,我们可以根据数据的大小来判断两个节点的位置,如果在根节点的2侧,则根节点为最近公共祖先,如果在根节点的同侧,继续寻找2个节点的位置

TreeNode* PublicLowParent(TreeNode *root, TreeNode*c1, TreeNode* c2)
{
    if (root == NULL)
        return NULL;
    TreeNode* cur = root;
    while (cur)
    {
        if (c1->data > cur->data&&c2->data > cur->data)
        {//x1,x2都在右子树
            cur = cur->right;
        }
        else if (c1->data < cur->data&&c2->data < cur->data)
        {//x1,x2都在左子树
            cur = cur->left;
        }
        else
            return cur;
    }
}

针对第二种情况
以前遇到过2个单链表相交求交点的问题,此时我们可以借鉴当时的链表求交点的办法,来寻找2个节点所在链表的交点,即为公共最近公共祖先。链表求交点,如果2个链表长度不相等,要将长链表缩短,直到与短链表的长度相等为止。

int GetLen(TreeNode *root,TreeNode* node)
{
    int count = 0;
    if (node == root)
        return 0;
    while (node != root)
    {
        count++;
        node = node->parent;
    }
    return count;
}
TreeNode* PublicLowParent(TreeNode *root, TreeNode*c1,TreeNode* c2 )
{
    TreeNode* p1 = c1;
    TreeNode* p2 = c2;
    int len1 = GetLen(root,c1);
    int len2 = GetLen(root,c2);
    int sz = len1 - len2;
    //p1比p2长,p1先走多余的长度步
    if (sz > 0)
    {
        while (sz--)
        {
            p1 = p1->parent;
        }
    }
    //p2比p1长,p2先走多余的长度步
    else
    {
        sz = 0 - sz;
        while (sz--)
        {
            p2 = p2->parent;
        }
    }
    //此时链表长度一样,一起向后走
    while (p1 != p2)
    {
        p1 = p1->parent;
        p2 = p2->parent;
    }
    //跳出循环时即为2链表相交点
    return p1;
}

对于第三种情况,我们借助栈来实现,保存这2个节点从根结点开始到他们的路径,根据栈中的数据来判断最近的公共交点,同样也用到了链表的求交点的思想

//从根结点开始找寻节点,将路径中的节点分别进行压栈
bool GetPath(TreeNode* root, TreeNode* node, stack<TreeNode*>& s)
{
    if (root == NULL)
        return false;
    s.push(root);
    if (root == node)
        return true;
    else //node != root
    {
        bool x = GetPath(root->left,node,s);
        bool y = GetPath(root->right, node, s);
        if (x==false&&y==false)//没有找到节点,弹出
        {
            s.pop();
            return false;
        }   
        return true;
    }
}
TreeNode* PublicLowParent(TreeNode *root, TreeNode*c1, TreeNode* c2)
{
    if (root == NULL)
        return NULL;
    stack<TreeNode*>s1;//保存c1的路径
    stack<TreeNode*>s2;//保存c2的路径
    GetPath(root, c1, s1);
    GetPath(root, c2, s2);
    if (s1.empty() || s2.empty())
        return NULL;
    //两个栈中元素个数之差
    int sz = s1.size() - s2.size();
    //类似2个链表求交点
    if (sz > 0)
    {
        while (sz--)
        {
            s1.pop();
        }
    }
    else
    {
        sz = 0 - sz;
        while (sz--)
        {
            s2.pop();
        }
    }
    while (!s1.empty() && !s2.empty() && s1.top() != s2.top())
    {
        s1.pop();
        s2.pop();
    }
    return s1.top();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值