zoukankan      html  css  js  c++  java
  • 【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)
  • 相关阅读:
    前端脚手架的那些事儿
    CSS重置默认样式reset.css代码模板
    Web 3.0 前瞻:基于区块链的下一代浏览器
    关键词定位是网站推广的基础
    6年架构师针对web前端小白,作出的职业规划建议
    和程序员约会的优点和缺点
    如何在软件开发中避免出现漏洞
    Linux下修改时区
    前端需要掌握的Nginx知识
    Nginx入门指南
  • 原文地址:https://www.cnblogs.com/flix/p/12865347.html
Copyright © 2011-2022 走看看