剑指 Offer 68 - I. 二叉搜索树的最近公共祖先( c++)

剑指 Offer 68 - I. 二叉搜索树的最近公共祖先( c++)_第1张图片
剑指 Offer 68 - I. 二叉搜索树的最近公共祖先( c++)_第2张图片

二叉查找树(Binary Search Tree),(又:二叉搜索树,二叉排序树)它或者是一棵空树,或者是具有下列性质的二叉树: 若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左、右子树也分别为二叉排序树

方法一:两次遍历

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        vector<TreeNode*>path_p=get_path(root,p);
        vector<TreeNode*>path_q=get_path(root,q);
        TreeNode*ancestor;//定义祖先节点
        //p 和 q 的最近公共祖先就是从根节点到它们路径上的「分岔点」,也就是最后一个相同的节点。因此,如果我们设从根节点到 p 的路径为数组path_p,从根节点到 q 的路径为数path_q,那么只要找出最大的编号 i,其满足path_q[i]=path_p[i]
        for(int i=0;i<path_p.size()&&i<path_q.size();i++)//遍历pq的path中的所有节点
        {
            //从根节点开始,当path_q[i]=path_p[i],继续往下遍历,直到路径节点不相等,即可以找到最近的公共祖先
            if(path_q[i]==path_p[i])
            {
                ancestor=path_p[i];
            }else{
                break;
            }
        }
        return ancestor;
    }

//对p节点,从根节点开始查找p节点,如果当前节点等于p节点,则返回当前节点,
//若当前节点大于p节点,则p节点在左子树上,遍历左子树,否则,遍历右子树
    vector<TreeNode*>get_path(TreeNode*node,TreeNode*target)
    {
        vector<TreeNode*>path;//定义一个容器path存储根节点到目标节点的路径上的所有节点
        path.push_back(node);//把根节点存入path中
        while(node!=target)//当前节点不等于目标节点时,进行遍历
        {
            if(node->val>target->val)//如果当前节点大于目标值,则目标节点在左子树上
            {
                node=node->left;
            }else{
                node=node->right;//否则在右子树上
            }
            path.push_back(node);//将根节点到目标节点的路径上的节点都存入path中
        }
        return path;
    }
};

复杂度分析

时间复杂度:O(n),其中 n 是给定的二叉搜索树中的节点个数。上述代码需要的时间与节点 p 和 q 在树中的深度线性相关,而在最坏的情况下,树呈现链式结构,p 和q 一个是树的唯一叶子结点,一个是该叶子结点的父节点,此时时间复杂度为Θ(n)。

空间复杂度:O(n),我们需要存储根节点到 p 和 q 的路径。和上面的分析方法相同,在最坏的情况下,路径的长度为 Θ(n),因此需要Θ(n) 的空间。

方法二:一次遍历 思路与算法

在方法一中,我们对从根节点开始,通过遍历找出到达节点 p 和 q 的路径,一共需要两次遍历。我们也可以考虑将这两个节点放在一起遍历。

整体的遍历过程与方法一中的类似:

我们从根节点开始遍历;

如果当前节点的值大于 p 和 q的值,说明 p 和 q 应该在当前节点的左子树,因此将当前节点移动到它的左子节点;

如果当前节点的值小于 p 和 q 的值,说明 p 和 q 应该在当前节点的右子树,因此将当前节点移动到它的右子节点;

如果当前节点的值不满足上述两条要求,那么说明当前节点就是「分岔点」。此时,p 和 q 要么在当前节点的不同的子树中,要么其中一个就是当前节点。

可以发现,如果我们将这两个节点放在一起遍历,我们就省去了存储路径需要的空间。

/**
 * 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:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
        TreeNode*ancestor;//定义祖先节点
        ancestor=root;//祖先节点指向根节点
        while(true)
        {
            if(ancestor->val > p->val && ancestor->val > q->val)
            {
                ancestor=ancestor->left;
            }
            else if(ancestor->val < p->val && ancestor->val < q->val)
            {
                ancestor=ancestor->right;
            }
            else
            {
                break;
            }
        }
        return ancestor;
    }
};

参考链接:https://leetcode-cn.com/problems/er-cha-sou-suo-shu-de-zui-jin-gong-gong-zu-xian-lcof/solution/er-cha-sou-suo-shu-de-zui-jin-gong-gong-0wpw1/

你可能感兴趣的:(力扣刷题笔记,c++,二叉搜索树,数据结构,二叉树)