[LeetCode] 二叉树的最近公共祖先结点问题合集

114 阅读1分钟

BST的最近公共祖先结点

链接:235. Lowest Common Ancestor of a Binary Search Tree

  1. 如果根节点等于p或q,那么该根节点就是p和q的最近公共祖先结点;
  2. 如果根节点值位于p和q的值之间,说明p和q分别在其左右子树上,那么根节点就是p和q的最近公共祖先;
  3. 如果根节点值大于p和q的值,说明p和q都在根节点的左子树上,我们去根节点的左子树上找;
  4. 如果根节点值小于p和q的值,说明p和q都在根节点的右子树上,我们去根节点的右子树上找。
var lowestCommonAncestor = function(root, p, q) {
    const max = Math.max(p.val,q.val), min = Math.min(p.val,q.val);
    let res = null;
    dfs(root);
    return res;

    function dfs(node) {
        if (res||!node) return;
        if (node.val===p.val) res = p;
        else if (node.val===q.val) res = q;
        else if (node.val>min&&node.val<max) res = node;
        else if (node.val>max) dfs(node.left);
        else dfs(node.right);
    }
};

二叉树的最近公共祖先结点

链接:236. Lowest Common Ancestor of a Binary Tree

思路一:首先通过dfs找到从根节点到p、q结点的路径,这两条路径的前面部分是他俩的公共祖先结点,只要找到最后一个相同的结点即可。

var lowestCommonAncestor = function(root, p, q) {
    const path1 = [], path2 = [];
    getPath(root,p,path1);
    getPath(root,q,path2);
    let res = null;
    const min = Math.min(path1.length,path2.length);
    for (let i = 0; i < min; i++) {
      if (path1[i]===path2[i]) res = path1[i];
      else return res;
    }
    return res;

    function getPath(node, targetNode, path) {
      if (!node) return false;
      path.push(node);
      if (targetNode===node) return true;
      else {
        if (getPath(node.left,targetNode,path)) return true;
        if (getPath(node.right,targetNode,path)) return true;
        path.pop();
        return false;
      }
    }
};

思路二:与第一题类似,dfs,如果p和q分别在某结点的左右子树上,那么该结点就是p和q的最近公共祖先;如果某节点等于p或q,且其子树上有q或p,则该结点也是p和q的最近公共祖先。因此我们每个dfs中要返回该子树上是否包含p和q。

var lowestCommonAncestor = function(root, p, q) {
    let res = null;
    dfs(root);
    return res;

    function dfs(node) {
      // 返回2:该子树上有p和q;返回1:该子树上有p和q之一;返回0:该子树上无p或q
      if (!node) return 0;
      if (res) return 2;
      let left = dfs(node.left);
      if (left===2) return 2;
      if (left===1&&(node===p||node===q)) {
        res = node;
        return 2;
      }
      let right = dfs(node.right);
      if (right===2) return 2;
      if (right===1&&(node===p||node===q)) {
        res = node;
        return 2;
      }
      if (left+right===2) {
        res = node;
        return 2;
      }
      return left+right+(node===p||node===q)?1:0;
    }
};

最深叶节点的最近公共祖先

链接:1123. Lowest Common Ancestor of Deepest Leaves

一种思路是参考第二题的思路一,找到所有从根节点到最深叶节点的路径,然后找到最后一个公共祖先结点。

还有一种更好的方法:对于一个结点,如果它的左右子树高度相等,那么它就是它所在根节点的最深叶节点的最近公共祖先结点。看代码。

var lcaDeepestLeaves = function(root) {
  let res = null, maxDepth = 0;
  dfs(root,0);
  return res;

  function dfs(node,depth) {
    // 返回该子树最深深度
    if (!node) return depth;
    let d1 = dfs(node.left,depth+1), d2 = dfs(node.right,depth+1);
    if (d1===d2&&d1>=maxDepth) {
      res = node;
      maxDepth = d1;
    }
    return Math.max(d1,d2);
  }
};