二叉树问题——寻找二叉树中两个节点的最近公共祖先

此题大概分为3种情况:
1、节点中无parent,但提供了root
(1)、此种情况又分为两种,开辟空间,使用容器来保存路径,将其转换为求链表公共节点的问题,时间复杂度为O(N),空间复杂度为O(N)
(2)、不开辟空间,在节点的左右子树上寻找两个节点,若两个节点存在在节点的左右子树,则该节点为最近的公共节点,否则继续在该节点的左右子树寻找,主要使用递归来完成寻找,时间复杂度为O(N*N)
综合分析,推荐使用第一种方法
2、节点中有parent,但未提供root
3、此二叉树是二叉搜索树

解决方法:
前面两种情况都是想办法将此题转换为求解两个相交链表的交点:
第一种情况需要我们将两个节点到root的路径保存起来,此时路径就好像是链表;
第二种情况比第一种情况还要简单些,我们只需要不断向前找,直至parent为空,统计两个节点的深度,此时依旧是将两个节点看做两个链表的起点,依旧是相交链表的问题;
第三种情况:我们只需由根节点开始不断和两个节点的值做对比,若节点值均大于两个节点的值,则在左子树继续寻找,若节点均小于两个节点的值,则在右子树继续寻找,若节点值恰好在两个节点之间,则此节点就是最近的公共祖先。
三种方法均写在一个类中了,节点共用,但三种方法对节点的使用时按照三种条件严格区分的,代码如下:

#pragma once

#include<iostream>
#include<stack>

using namespace std;


//Lowest Common Ancestor最小公共节点问题,简称LCA
//三种情况,普通二叉树,无parent,已知root;有parent,无root;二叉搜索树

template<class T>
struct BSNode
{
    int key;
    BSNode* left;
    BSNode* right;
    BSNode* parent;
    BSNode(T value):
        key(value)
        , left(NULL)
        , right(NULL)
        , parent(NULL)
    {}
};

template<class T>
class BSTree
{
    typedef BSNode<T> Node;
public:
    BSTree():
        _root(NULL)
    {}
    ~BSTree()
    {}
public:
    void Insert(T key)
    {
        Node* newnode = new Node(key);
        if (_root == NULL)
        {
            _root = newnode;
        }
        else
        {
            Node* cur = _root;
            Node* parent = NULL;
            while (cur)
            {
                parent = cur;
                if (key > cur->key)
                {
                    cur = cur->right;
                }
                else if (key < cur->key)
                {
                    cur = cur->left;
                }
                else
                {
                    return;
                }
            }
            if (parent->key > key)
            {
                parent->left = newnode;
            }
            else
            {
                parent->right = newnode;
            }
            newnode->parent = parent;
        }
    }
    void Print()
    {
        if (_root == NULL)
        {
            return;
        }
        _Print(_root);
    }
    void _Print(Node* cur)
    {
        if (cur == NULL)
        {
            return;
        }
        else if (cur->left)
        {
            _Print(cur->left);
        }
        cout << cur->key << " ";
        if (cur->right)
        {
            _Print(cur->right);
        }
    }
    Node* Find(int key)
    {
        Node*  cur = _root;
        while (true)
        {
            if (cur->key > key)
            {
                cur = cur->left;
            }
            else if (cur->key < key)
            {
                cur = cur->right;
            }
            else
            {
                return cur;
            }
        }
        return NULL;
    }
    // 1、普通二叉树,第一种方法,使用容器来保存路径
        //(1)、由根节点寻找给定的两个节点,用栈保存路径节点的值
        //(2)、此时问题转换为求两个链表的公共节点问题,将较长的
        void Path(Node* root, Node* node,stack<Node*> &s,bool &f)
        {
            s.push(root);
            if (root == node)
            {
                f = false;
                return;
            }
            if (f && root->left != NULL)
            {
                Path(root->left, node, s,f);
            }
            if (f && root->right != NULL)
            {
                Path(root->right, node, s,f);
            }
            if (f)
            {
                s.pop();
            }
        }
        Node* FindLCA1(Node* node1, Node* node2)
        {
            if (node1 == NULL || node2 == NULL || _root == NULL)
            {
                return NULL;
            }
            stack<Node*> s1;
            stack<Node*> s2;
            bool f = true;
            Path(_root, node1, s1,f);
            f = true;
            Path(_root, node2, s2,f);
            //将两个栈调整为长度一样的
            int count = 0;
            if (s1.size() > s2.size())
            {
                count = s1.size() - s2.size();
                while (count--)
                {
                    s1.pop();
                }
            }
            else
            {
                count = s2.size() - s1.size();
                while (count--)
                {
                    s2.pop();
                }
            }
            //逐个比较,寻找相同节点
            while (s1.top() != s2.top())
            {
                s1.pop();
                s2.pop();
            }
            Node* LCA = s1.top();
            return LCA;
        }
        //第一类第二种方法:普通二叉树无需开辟空间的方法,递归在节点左右子树查找两个节点是否分别在左右子树中,若存在,则该节点是最近公共祖先,否则递归左右子树继续查找
        //1、从根节点开始在左右子树上查找两个节点是否存在
        //2、若存在,则该节点就是最近公共祖先,否则继续在该节点的左右子树继续查找
        Node* FindR(Node* root, Node* node)
        {
            if (root == NULL || node == NULL)
            {
                return NULL;
            }
            while (root!=NULL)
            {
                if (root->key > node->key)
                {
                    root = root->left;
                }
                else if (root->key < node->key)
                {
                    root = root->right;
                }
                else
                {
                    return root;
                }
            }
            return NULL;
        }
        Node* FindLCA4R(Node* root, Node* node1, Node* node2)
        {
            if (root == NULL)
            {
                return NULL;
            }
            else if (FindR(root->left, node1) && FindR(root->right, node2))
            {
                return root;
            }
            if (FindLCA4R(root->left, node1, node2) == NULL)
            {
                FindLCA4R(root->right, node1, node2);
            }
        }
        Node* FindLCA4(Node* node1, Node* node2)
        {
            if (_root == NULL || node1 == NULL || node2 == NULL)
            {
                return NULL;
            }
            else if (node1 == _root || node2 == _root)
            {
                return _root;
            }
            return FindLCA4R(_root, node1, node2);
        }
    //第三种情况,二叉搜索树,充分利用二叉搜索树的特性
    //(1)、由根节点开始逐个和两个节点的key值对比
    //(2)、若均大于,则在该节点的左子树;若均小于,则在该节点的右子树;若此时节点的值恰好在两个节点的中间,则此节点必然为最小公共节点
    Node* FindLCA3(Node* node1, Node* node2)
    {
        if (_root == NULL || node1 == NULL || node2 == NULL)
        {
            return NULL;
        }
        BSNode<T>* cur = _root;
        while (true)
        {
            if (cur->key > node1->key && cur->key > node2->key)
            {
                cur = cur->left;
            }
            else if (cur->key < node1->key && cur->key < node2->key)
            {
                cur = cur->right;
            }
            else
            {
                return cur;
            }
        }
        return NULL;
    }


    //第二种,有parent,但无root(用当前节点向前找,直至父节点为NULL)

    //(1)、有两个节点向前找,计算出两个节点的深度
    //(2)、此时问题和第一种情况类似
    Node* FindLCA2(Node* node1, Node* node2)
    {
        if (node1 == NULL || node2 == NULL)
        {
            return NULL;
        }
        int node1Len = 0;
        int node2Len = 0;
        Node* cur = node1;
        while (cur)
        {
            ++node1Len;
            cur = cur->parent;
        }
        cur = node2;
        while (cur)
        {
            ++node2Len;
            cur = cur->parent;
        }
        //调整节点
        int count = 0;
        if (node1Len > node2Len)
        {
            count = node1Len - node2Len;
            while (count--)
            {
                node1 = node1->parent;
            }
        }
        else
        {
            count = node2Len - node1Len;
            while (count--)
            {
                node2 = node2->parent;
            }
        }
        //由下向上寻找相同的节点
        while (node1 != node2)
        {
            node1 = node1->parent;
            node2 = node2->parent;
        }
        return node1;
    }
private:
    BSNode<T>* _root;
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值