二叉树深度学习——二叉树的最近公共祖先

发布于:2024-10-09 ⋅ 阅读:(59) ⋅ 点赞:(0)

1.题目解析

 题目来源:236.二叉树的最近公共祖先

测试用例 

2.算法原理

基本解法O(N^2)

这里我们可以将寻找的两节点分为在同一子树与不在同一子树的两种情况分析

1.在同一子树:由于自己可以是自己的祖先,所以寻找层数较高的节点就是二者的公共节点

2.不在同一个子树:找到两个节点所在子树个最近一个祖先节点即可

具体思路是:创建一个查找节点的函数,然后从最上面的头结点查找两节点所在的子树,使用递归向下不断查找直到找到为止,然后根据上述的两种情况,不在同一子树返回最近公共祖先,在同一子树则递归查找即可

进阶解法O(logN)

 由于上面的思路时间复杂度为O(N^2),所以我们可以进行优化,这里优化的思路是记录两个节点从最上面的头结点到自己的路径,然后求出两节点路径的交点即可,因为由题目可以知道两个节点一定都在二叉树中,所以可以使用这样的思路

记录路径的方法:首先从头开始不断插入节点,如果在左子树找到该节点则入栈记录路径后直接退出,同理如果在右子树找到也是,若在左右子树都没找到则删除之前在这棵子树记录的路径然后寻找其他子树即可,最后直到找到两个节点,此时两个不同的栈就记录了两节点的路径

找出交点的方法:先让长的栈先删除出栈,直到两个栈长度相同,然后一起出栈删除直到二者的头元素相同然后返回任意一个头元素即可

3.实战代码

基本解法代码: 

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool FindInTree(TreeNode* t,TreeNode* x)
    {
        if(t == nullptr)
        {
            return false;
        }

        return t == x
        || FindInTree(t->left,x)
        || FindInTree(t->right,x);
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        if(root == nullptr)
        {
            return nullptr;
        }
        if(root == p || root == q)
        {
            return root;
        }

        bool pInLeft = FindInTree(root->left,p);
        bool pInRight = !pInLeft;

        bool qInLeft = FindInTree(root->left,q);
        bool qInRight = !qInLeft;

        if((pInLeft && qInRight) || (pInRight && qInLeft))
        {
            return root;
        }
        else if(pInLeft && qInLeft)
        {
            lowestCommonAncestor(root->left,p,q);
        }
        else if(pInRight && qInRight)
        {
            lowestCommonAncestor(root->right,p,q);
        }
        
        return nullptr;
    }
};

进阶解法代码: 

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
class Solution {
public:
    bool GetPath(TreeNode* root,TreeNode* x,stack<TreeNode*>& st)
    {
        //节点为空直接返回
        if(root == nullptr)
        {
            return false;
        }

        //不管是什么直接入栈记录路径
        st.push(root);

        //如果找到节点则不用继续寻找该节点的路径
        if(root == x)
        {
            return true;
        }

        //如果没找到节点则继续从该节点的左子树寻找并记录路径
        if(GetPath(root->left,x,st))
        {
            return true;
        }

        //同理去他的右子树寻找并记录数据
        if(GetPath(root->right,x,st))
        {
            return true;
        }

        //如果在左右子树均未找到则这个节点整个子树都不是目标节点的路径
        //删除这个子树后继续寻找其他子树
        st.pop();
        return false;
    }
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) 
    {
        stack<TreeNode*> pst,qst;
        bool pInTree = GetPath(root,p,pst);
        bool qInTree = GetPath(root,q,qst);

        //让长的路径先删除
        while(pst.size() != qst.size())
        {
            if(pst.size() > qst.size())
            {
                pst.pop();
            }
            else
            {
                qst.pop();
            }
        }

        //同时删除直到找到路径中的共同节点
        while(pst.top() != qst.top())
        {
            pst.pop();
            qst.pop();
        }
        return pst.top();
    }
};