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

本文探讨了在三种不同类型的二叉树中寻找两个节点最近公共祖先的方法:1) 三叉链结构,通过保存路径对比;2) 搜索二叉树,利用节点大小关系进行递归查找;3) 普通二叉树,结合前两种策略解决问题。

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

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

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

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

3:就是普通二叉树。(尽可能实现时间复杂度为O(N))


求两个结点的最近公共祖先有两种情况。
1、如果这两个结点不在一条线上,则它们就分别在最近的公共祖父的左右子树上。
2、如果这两个结点在一条线上,则它们之中深度最前的就是他们的最近的公共祖先。

1:三叉链。在这种情况下,我们可以分别用两个数据结构来保存两个节点到根节点的路径,然后通过对比,找到最近公共祖先。

/*
//三叉链
struct Node
{
    int _data;
    Node* _left;
    Node* _right;
    Node* _parent;
    Node(const int& x)
        : _data(x)
        , _left(NULL)
        , _right(NULL)
        , _parent(NULL)
    {}
};
*/
Node* GetLastestCommonAncestor4(Node* root, Node* x1, Node* x2)
{
    assert(x1 && x2);
    if (root == NULL)
        return NULL;

    stack<Node*> s1;
    stack<Node*> s2;

    //x1
    Node* cur1 = x1;
    while (cur1->_parent)
    {
        s1.push(cur1);
        cur1 = cur1->_parent;
    }

    //x2
    Node* cur2 = x2;
    while (cur2->_parent)
    {
        s2.push(cur2);
        cur2 = cur2->_parent;
    }

    //走到这里,两个栈中分别保存了x1和x2到根节点的路径
    while (s1.size() != s2.size())
    {
        if (s1.size() > s2.size())
            s1.pop();
        else
            s2.pop();
    }

    while (!s1.empty() && !s2.empty() && s1.top() != s2.top())
    {
        s1.pop();
        s2.pop();
    }

    if (s1.top() == s2.top())
        return s1.top();
    return NULL;
}

2:二叉树是搜索二叉树,对于搜索树来说,左子树的节点都比根节点小,右子树的节点都比根节点大。根据搜索树这样的特征,我们可以有以下思路。
(1)如果一个节点比根节点小,另一个节点比根节点大, 则根节点为最近公共祖先;
(2)如果两个节点均比根节点小,则最近公共祖先在左子树;
(3)如果两个节点均比根节点大,则最近公共祖先在右子树。
有了这样的思路,我们可以通过递归,实现查找最近公共祖先。

struct Node
{
     int _data;
     Node* _left;
     Node* _right;
     Node(const int& x)
          : _data(x)
          , _left(NULL)
          , _right(NULL)
     {}
};

Node* GetLastestCommonAncestor1(Node* root, Node* x1, Node* x2)
{
     assert(x1&&x2);
     if (root == NULL)
          return NULL;
     if ((x1->_data <= root->_data&&x2->_data >= root->_data)
          || (x1->_data >= root->_data&&x2->_data <= root->_data))
          return root;
     else if (x1->_data < root->_data&&x2->_data < root->_data)
          return GetLastestCommonAncestor1(root->_left, x1, x2);
     else
          return GetLastestCommonAncestor1(root->_right, x1, x1);
}

3:普通二叉树
可以分别使用上面两种特殊情况的思想来处理普通二叉树

struct Node
{
     int _data;
     Node* _left;
     Node* _right;
     Node(const int& x)
          : _data(x)
          , _left(NULL)
          , _right(NULL)
     {}
};

//普通二叉树(搜索二叉树思想)
bool IsInTree(Node* root, Node* x)
{
     if (root == NULL)
          return false;
     if (root->_data == x->_data)
          return true;
     else
          return IsInTree(root->_left, x) || IsInTree(root->_right, x);
}

Node* GetLastestCommonAncestor2(Node* root, Node* x1, Node* x2)
{
     assert(x1 && x2);
     if (root == NULL)
          return NULL;
     bool x1InLeft, x1InRight, x2InLeft, x2InRight;
     x1InLeft = IsInTree(root->_left, x1);
     x1InRight = IsInTree(root->_right, x1);
     assert(x1InLeft || x1InRight);

     x2InLeft = IsInTree(root->_left, x2);
     x2InRight = IsInTree(root->_right, x2);
     assert(x2InLeft || x2InRight);

     if ((x1InLeft && x2InRight) || (x1InRight && x2InLeft))
          return root;
     else if (x1InLeft&&x2InLeft)
          return GetLastestCommonAncestor2(root->_left, x1, x2);
     else
          return GetLastestCommonAncestor2(root->_right, x2, x2);
}

//普通二叉树(三叉链方法的思想)
bool GetNodePaths(Node* root, Node* x, stack<Node*>& s)
{
     if (root == NULL)
          return;
     s.push(root);
     if (root == x)
          return true;
     bool inLeft = GetNodePaths(root->_left, x, s);
     if (inLeft)
          return true;
     bool inRight = GetNodePaths(root->_right, x, s);
     if (inRight)
          return true;
     s.pop();
     return false;
}

Node* GetLastestCommonAncestor3(Node* root, Node* x1, Node* x2)
{
     assert(x1&&x2);
     if (root == NULL)
          return NULL;
     stack<Node*> paths1, paths2;
     if (!GetNodePaths(root, x1, paths1)   || !GetNodePaths(root, x2, paths2))
          return NULL;

     while (paths1.size() != paths2.size())
     {
          if (paths1.size() > paths2.size())
              paths1.pop();
          else
              paths2.pop();
     }

     while (!paths1.empty() && !paths2.empty() && paths1.top() != paths2.top())
     {
          paths1.pop();
          paths2.pop();
     }

     if (paths1.top() == paths2.top())
          return paths1.top();
     return NULL;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值