【LeetCode-树】二叉树的最近公共祖先

题目描述

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。
百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”
例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

示例:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

题目链接: https://leetcode-cn.com/problems/lowest-common-ancestor-of-a-binary-tree/

思路1

如果一个节点 node 是节点 p 和节点 q 的公共祖先,则 node 和 p、q 满足下面 3 种情况:

  • node->val == p->val,q 在以 node 为根的子树里;
  • node->val == q->val,p 在以 node 为根的子树里;
  • node->val != p->val && node->val != q->val,则 p 在 node 的左子树里且 q 在 node 的右子树里 或者 p 在 node 的右子树里且 q 在 node 的左子树里。

题目要求找到最低的公共祖先,所以我们要遍历树中所有的节点,判断每个节点是否满足条件。下面的代码使用 bfs 遍历。在遍历过程中,越晚遍历意味着节点越深,所以记录满足条件且最晚遍历到的节点作为答案。代码如下:

/**
 * 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) {
        if(root==nullptr) return nullptr;

        queue<TreeNode*> qq;
        qq.push(root);
        TreeNode* ans = nullptr;
        while(!qq.empty()){
            TreeNode* curNode = qq.front(); qq.pop();
            if(curNode->val==p->val){
                if(search(curNode->left, q) || search(curNode->right, q)) ans = curNode;
            }else if(curNode->val==q->val){
                if(search(curNode->left, p) || search(curNode->right, p)) ans = curNode;
            }else{
                if((search(curNode->left, p)&&search(curNode->right, q)) || (search(curNode->left, q)&&search(curNode->right, p))) ans = curNode;
            }

            if(curNode->left!=nullptr) qq.push(curNode->left);
            if(curNode->right!=nullptr) qq.push(curNode->right);
        }
        return ans;
    }

    /*在树root中查找是否存在值等于target->val的节点,存在返回true,不存在返回false*/
    bool search(TreeNode* root, TreeNode* target){
        if(root==nullptr) return false;
        if(root->val==target->val) return true;

        return search(root->left, target) || search(root->right, target);
    }
};
  • 时间复杂度:O(n^2)
    n 为节点个数,bfs 遍历需要 O(n),bfs 遍历每一个节点还需要 O(n) 的时间来判断,所以为 O(n^2).
  • 空间复杂度:O(n+h)
    n 为节点个数,h 为树高。

思路2

换一种思路递归。在 root 的左右子树中寻找 p 或者 q。在左子树中寻找的结果记为 left,在右子树寻找的结果记为 right。如果 left 为空,说明两个节点都在右子树当中:一个节点是右子树的根节点,另一个节点是右子树的子节点,返回 right。同理,如果 right 为空,则返回 left;如果 right 和 left 都不为空,则返回 root。代码如下:

/**
 * 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) {
        if(root==nullptr) return nullptr;
        if(root==p || root==q) return root;

        TreeNode* left = lowestCommonAncestor(root->left, p, q);
        TreeNode* right = lowestCommonAncestor(root->right, p, q);

        if(left==nullptr) return right;
        if(right==nullptr) return left;
        if(left!=nullptr && right!=nullptr) return root;
        return nullptr;
    }
};
  • 时间复杂度:O(n)
  • 空间复杂度:O(n)
posted @ 2020-05-10 22:17  Flix  阅读(165)  评论(0编辑  收藏  举报