17. 二叉树的最近公共祖先【LC236】

146 阅读2分钟

题目: 给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。 百度百科中最近公共祖先的定义为:“对于有根树 T 的两个节点 p、q,最近公共祖先表示为一个节点 x,满足 x 是 p、q 的祖先且 x 的深度尽可能大(一个节点也可以是它自己的祖先)。”

关键思路:(递归) O(n)

递归函数:返回当前子树中 p 和 q 的 LCA。如果没有 LCA,就返回 null。

从根节点 root 开始往下递归遍历……

如果遍历到 p 或 q,比方说 p,则 LCA 要么是当前的 p(q 在 p 的子树中),要么是 p 之上的节点(q 不在 p 的子树中),不可能是 p 之下的节点,不用继续往下走,返回当前的 p。

当遍历到 null 节点,空树不存在 p 和 q,没有 LCA,返回 null。

当遍历的节点 root 不是 p 或 q 或 null,则递归搜寻 root 的左右子树:

  • 如果左右子树的递归都有结果,说明 p 和 q 分居 root 的左右子树,向上返回 root。
  • 如果只是一个子树递归调用有结果,说明 p 和 q 都在这个子树,返回该子树递归结果。
  • 如果两个子树递归结果都为 null,说明 p 和 q 都不在这俩子树中,返回 null。

注意:全部以子树的角度切入、分析,root 即当前子树的跟节点,也就是当前节点,这样思路会清晰一点

原则:p和q节点一定在当前树中

下面是我画了一棵树,平铺递归,大家从右往左看,结合代码实现,就很清晰当前的递归了。

image.png

注意: 以子树的维度,到了末端,叶子节点相当于有两个null子节点,补充完null叶子节点,就方便抽象规律了。

解:

var lowestCommonAncestor = function (root, p, q) {
  // root 即当前子树的跟节点,也就是当前节点
  // 全部以子树的角度切入、分析
  if (!root) {
    return null;
  }
  if (root == p || root == q) {
    return root; // 返回给上面链路的承接变量leftright
  }

  let left = lowestCommonAncestor(root.left, p, q); //找左子树中的最近公共父节点
  let right = lowestCommonAncestor(root.right, p, q); //找右子树中的最近公共父节点
  //以子树的维度,到了末端,叶子节点相当于有两个null子节点,补充完null叶子节点,就方便抽象规律了。
  //如果当前节点的leftright分别为pq,向上返回当前节点;
  //如果当前节点的leftright中一个为p或者q,向上返回q或者p;
  //如果当前节点的leftright不为pq,向上返回null;

  if (left && right) { // p, q分居当前节点的左右子树中
    return root
  };
  return left || right || null;
};

———— 前端、Javascript实现、算法、刷题、leetcode