leetcode 236. 二叉树的最近公共祖先 思考分析


本文章代码思路来源于公众号【代码随想录】

题目

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

百度百科中最近公共祖先的定义为:“对于有根树 T 的两个结点 p、q,最近公共祖先表示为一个结点 x,满足 x 是 p、q 的祖先且 x
的深度尽可能大(一个节点也可以是它自己的祖先)。”
在这里插入图片描述
在这里插入图片描述

思考分析

1、自下而上遍历对于此题有极大帮助,而后序遍历的特征便是优先处理叶子结点
2、如何判断一个节点是结点q和结点p的公共祖先:
如果找到一个结点,左子树出现结点p,右子树出现结点q,或者左子树出现结点q,右子树出现结点p。
确定返回值和参数
如果单纯只是告诉我们是否找到结点q或者p,那么返回值为bool就行了。
但是我们还要返回最近公共结点,所以返回值为TreeNode* 类型,如果遇到p或者q,就把p或者q返回,返回值不为空就说明了找到了q或者p。

TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)

确定终止条件
如果找到结点p或者结点q或者结点为空,返回

if(root == q || root == p || root == NULL) return root;

确定单层逻辑
首先确定一点我们需要遍历树的所有结点。
直观上讲,如果找到了最近公共祖先,直接一路返回就可以了。如下图
在这里插入图片描述

但是事实上还要遍历根结点的右子树(即使现在已经找到了目标结点),这是因为在后序遍历中,如果想要利用left和right做逻辑处理,不能立即返回,而是要等到left和right的逻辑处理完之后才能返回。

left = 递归函数(root->left);
right = 递归函数(root->right);
left 和 right 的逻辑处理

所以我们要遍历整棵树。

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

1、如果left和right都不为空,说明此时root就是最近公共祖先。
2、如果left为空,right不为空,就返回right,说明目标结点是通过right返回的,反之依然。
在这里插入图片描述
3、如果left和right都为空,则返回left或者right都是可以的,也就是返回空。

if(left == NULL && right != NULL) return right;
else if(left != NULL && right ==NULL) return left;
else return NULL;		//此时只有left和right同时为空

整个遍历思路:
在这里插入图片描述

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 //如果一个结点不是另外一个结点的祖先,那么他们的最近祖先的深度一定是深度最小结点的深度-1
 //如果一个结点是另外一个结点的祖先,那么最近祖先就是是祖先的那个结点。
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(root == q || root == p || root == NULL ) return root;
       TreeNode* left = lowestCommonAncestor(root->left,p,q);
       TreeNode* right = lowestCommonAncestor(root->right,p,q);
       if(left != NULL && right != NULL) return root;
       else if(left == NULL && right != NULL) return right;
       else if(left != NULL && right ==NULL) return left;
       else return NULL;		//此时只有left和right同时为空
    }
};

改进

在遍历完左子树之后,若是返回的left不是NULL,也不是p和q,则说明在左子树中已经找到了公共祖先了,此时可以直接返回left的值,无需再遍历右子树。这就是剪枝操作。

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode(int x) : val(x), left(NULL), right(NULL) {}
 * };
 */
 //如果一个结点不是另外一个结点的祖先,那么他们的最近祖先的深度一定是深度最小结点的深度-1
 //如果一个结点是另外一个结点的祖先,那么最近祖先就是是祖先的那个结点。
class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(root == q || root == p || root == NULL ) return root;
       TreeNode* left = lowestCommonAncestor(root->left,p,q);
       //*****************剪枝操作******************
       if(left !=NULL && left !=p && left != q) return left; 
       //*******************************************
       TreeNode* right = lowestCommonAncestor(root->right,p,q);
       if(left != NULL && right != NULL) return root;
       else if(left == NULL && right != NULL) return right;
       else if(left != NULL && right ==NULL) return left;
       else return NULL;		//此时只有left和right同时为空
    }
};

加入打印信息,对遍历的过程更加了解熟悉;

class Solution {
public:
    TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
       if(root == NULL) cout<<"NULL"<<endl;
       else cout<<root->val<<endl;
       if(root == q || root == p || root == NULL ) return root;
       TreeNode* left = lowestCommonAncestor(root->left,p,q);
       //*****************剪枝操作******************
       if(left !=NULL && left !=p && left != q) return left; 
       //*******************************************
       TreeNode* right = lowestCommonAncestor(root->right,p,q);
       if(left != NULL && right != NULL) return root;
       else if(left == NULL && right != NULL) return right;
       else if(left != NULL && right ==NULL) return left;
       else return NULL;		//此时只有left和right同时为空
    }
};

这一题感觉之后还需要多看几遍,多体会体会,感觉有些地方还是没理解。
若是有新的理解,再更新。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

拾牙慧者

欢迎请作者喝奶茶

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值