【二叉树公共祖先问题总结(全注释)—Leetcode刷题方法汇总(1024特辑)】

本文总结了二叉树和二叉搜索树中寻找最近公共祖先的思路与方法,包括递归策略和代码实现。通过回溯和二叉树性质,解析了如何自底向上找到公共祖先,并提供了具体的代码框架。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

请添加图片描述



前言

🌹节日快乐兄弟们!!!

我们在做二叉树oj时,题目要求用递归法,但是我们实际做题时,总是会对递归何时递归一条边,何时递归整棵树迷迷糊糊,卡卡今天来总结下:如何区分递归要搜索一条边,还是搜索整个树呢?

在递归函数有返回值的情况下:
1-如果要搜索一条边,递归函数返回值不为空的时候,立刻返回,
2-如果搜索整个树,直接用一个变量left、right接住返回值,这个left、right后序还有逻辑处理的需要,也就是后序遍历中处理中间节点的逻辑(也是回溯)

  1. 搜索一条边
    递归返回值不为空时:

代码框架1:

if (递归函数(root->left)) return ...;
if (递归函数(root->right)) return ...;

代码框架2:

if (...) return 递归函数(root->left) ;
if (...) return 递归函数(root->right) ;

递归返回值为空时:

代码框架1:

if (...)  递归函数(root->left) 
else if (...)  递归函数(root->right)
外面统一return;

代码框架2:

if (...) 
{
      递归函数(root->left);
      return;
} 
else if (...) 
{
       递归函数(root->right);
       return;
}
  1. 搜索整棵树

代码框架1:

left = 递归函数(root->left);  // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理;         // 中

一、二叉树的最近公共祖先

1.1 思路

递归思路:

    1-参数:三个节点指针,
    	返回值:节点指针(如果该节点的子树有p或q,则返回该节点指针,如果遍历到空也没有,则返回nullptr)
    2-终止条件:当前节点为空,返回空;当前节点为p或q,返回当前节点
    3-递归逻辑:后序
        先记录递归遍历左右,再判断:如果左右记录均不为空,则返回当前节点,
        如果左不为空,则返回右递归,反之亦然,
        均为空直接返回空,不再继续向下遍历(已判断该路径无效)
    4-返回值:前面条件中一定返回了

1.2 核心点

  1. 那么二叉树如何可以自底向上查找呢?回溯啊,二叉树回溯的过程就是从低到上。
  2. 后序遍历(左右中)就是天然的回溯过程,可以根据左右子树的返回值,来处理中节点的逻辑
  3. 将递归返回bool类型和节点指针结合

需要递归函数返回值,来告诉我们是否找到节点q或者p,那么返回值为bool类型就可以了。
但我们还要返回最近公共节点,可以利用上题目中返回值是TreeNode * ,那么如果遇到p或者q,就把q或者p返回,返回值不为空,就说明找到了q或者p

  1. 需要搜索整个树,所以代码逻辑为:

left = 递归函数(root->left); // 左
right = 递归函数(root->right); // 右
left与right的逻辑处理; // 中

1.3 代码实现

// 写法1:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
    if (nullptr == root || root == p || root == q) return root;

    // 先记录左右(为了遍历整个左右子树!!!~,画图理解!!!~)
    TreeNode* pleft = lowestCommonAncestor(root->left, p, q);
    TreeNode* pright = lowestCommonAncestor(root->right, p, q);

    // 中
    if (pleft && pright)
    {
        return root;
    }
    else if (pleft)
    {
        return pleft; // 画图理解!!!~
    }
    else if (pright)
    {
        return pright; // 画图理解!!!~
    }
    else
    {
        return nullptr;
    }
    // 如果不放心外面没有返回值,用如下的写法2代码
}


// 写法2:
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;
    if (left == NULL) return right;
    return left;
}

二、 二叉搜索树的最近公共祖先

1.1 思路

  • 对于二叉搜索树的最近祖先问题,其实要比普通二叉树公共祖先问题 (opens new window)简单的多。
  • 不用使用回溯,二叉搜索树自带方向性可以方便的从上向下查找目标区间,遇到目标区间内的节点,直接返回

1.2 核心点

  1. 因为是有序树,所有 如果 中间节点是 q 和 p 的公共祖先,那么 中节点的数组 一定是在 [p, q]区间的。即 中节点 > p && 中节点 < q 或者 中节点 > q && 中节点 < p。

二叉搜索树每个节点的左子树必然大于右子树,所以公共祖先的val一定在p的val和q的val之间

  1. 那么只要从上到下去遍历,遇到 cur节点是数值在[p, q]区间中则一定可以说明该节点cur就是q 和 p的公共祖先。 画图分析得:一定是最近公共祖先!!!

1.3 代码实现

// 递归法:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q)
{
    if (nullptr == root || root == p || root == q) return root;

    if (root->val > p->val && root->val > q->val) return lowestCommonAncestor(root->left, p, q); // 搜索一条边
    if (root->val < p->val && root->val < q->val) return lowestCommonAncestor(root->right, p, q);

    // 原始写法:
    //if (cur->val > p->val && cur->val > q->val) {   // 左
    //    TreeNode* left = traversal(cur->left, p, q);
    //    if (left != NULL) {
    //        return left;
    //    }
    //}

    return root;
}


// 迭代法:
TreeNode* lowestCommonAncestor(TreeNode* root, TreeNode* p, TreeNode* q) {
    while (root) {
        if (root->val > p->val && root->val > q->val) {
            root = root->left;
        }
        else if (root->val < p->val && root->val < q->val) {
            root = root->right;
        }
        else return root;
    }
    return NULL;
}

总结

这里对文章进行总结:
以上就是今天总结的内容,本文包括了我自己对二叉树公共祖先问题总结的小经验,分享给大家。
真💙欢迎各位给予我更好的建议,欢迎!小编创作不易,觉得有用可以一键三连哦,感谢大家。peace
希望大家一起坚持学习,共同进步。梦想一旦被付诸行动,就会变得神圣。

欢迎各位大佬批评建议,分享更好的方法!!!🙊🙊🙊
🌹1024你我用代码改变世界,节日快乐兄弟们!!!

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值