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

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

前言

祖先的定义: 若节点 p 在节点 root 的左(右)子树中,或 p = root,则称 root 是 p 的祖先。

最近公共祖先的定义: 设节点 root 为节点 p,q 的某公共祖先,若其左子节点 root.left 和右子节点 root.right 都不是 p,q 的公共祖先,则称 root 是 “最近的公共祖先” 。

根据以上定义,若 root 是 p,q 的 最近公共祖先 ,则只可能为以下情况之一:

  1. p 和 q 在 root 的子树中,且分列 root 的 异侧(即分别在左、右子树中);
  2. p=root ,且 q 在 root 的左或右子树中;
  3. q=root ,且 p 在 root 的左或右子树中;

网络异常,图片无法展示
|

Leetcode-235. 二叉搜索树的最近公共祖先

分析

先来回顾下二叉搜索树的相关性质:

二叉搜索树按照中序遍历是有序的,左孩子所有结点的值都小于根结点,右孩子所有结点的值大于根结点

本题使用递归的方法:

  • 如果p,q的值均小于根结点,则在左孩子中找公共祖先
  • 如果p,q的值均大于根结点,则在右孩子中找公共祖先
  • 否则,返回根结点,包含三种情况(1)p=root (2) q=root (3) p,q在root的两侧。

代码实现(Javascript版)

var lowestCommonAncestor = function(root, p, q) {
    if(p.val < root.val && q.val < root.val){
        return lowestCommonAncestor(root.left, p, q);
    }
    if(p.val > root.val && q.val > root.val){
        return lowestCommonAncestor(root.right, p, q);
    }
    return root
};
复制代码

Leetcode-236. 二叉树的最近公共祖先

分析

对于二叉树的两个节点node1和node2,找到他们的最低公共祖先节点,会有两种情况:

  1. node1是node2的最低公共祖先,或node2是node1的最低公共祖先
  2. node1与node2不互为最低公共祖先(不在一个子树上,需要通过向上汇聚才能找到最低公共祖先)

思路

二叉树没有二叉搜索树的性质,这里依然使用递归解决,通过递归对二叉树进行先序遍历,当遇到节点 node1 或 node2时返回。从底至顶回溯,当节点 node1,node2 在节点 root 的异侧时,节点 root 即为最近公共祖先,则向上返回 root。

lowestAncestor函数实现了:

  1. 若node1、node2其中之一等于根结点root,则返回node1,node2 (找到node1或node2,此时返回的不一定是最近公共祖先,有可能另一个结点找不到)
  2. 否则,返回最近公共祖先

递归解析

终止条件:

  1. 当遍历到叶节点,则直接返回null
  2. 当root等于node1、node2,则直接返回root递推工作:
  3. 开启递归左子节点,返回值记为left
  4. 开启递归右子节点,返回值记为right

分析返回值:

根据left和right,可展开为四种情况:

  1. 当left和right同时为空,说明root的左/右子树都不包含node1、node2,返回null
  2. 当left和right同时不为空,说明node1、node2分列在root的异侧(分别在左/右子树),因此root为最近公共祖先,返回root
  3. left为空,right不为空,node1、node2都不在root的左子树中,直接返回right。此处一般有两种情况:
  • 1)node1、node2其中一个在root的右子树中,此时right指向node1(假设为node1)
  • 2)node1、node2两节点都在root的右子树中,此时的right指向最近公共祖先节点
  1. left不为空,right为空,与情况3雷同

代码实现

Java版

// 易理解版
public static Node lowestAncestor(Node head, Node o1, Node o2){
    if(head == null || head == o1 || head == o2){
        return head;
    }
    Node left = lowestAncestor(head.left, o1, o2);
    Node right = lowestAncestor(head.right, o1, o2);
   if(left == null && right == null) return null; // 1.
   if(left == null) return right; // 3.
   if(right == null) return left; // 4.
   return root; // 情况2. 相当于if(left != null and right != null)
}
// 进一步简写
 public static Node lowestAncestor(Node head, Node o1, Node o2){
    if(head == null || head == o1 || head == o2){
        return head;
    }
    Node left = lowestAncestor(head.left, o1, o2);
    Node right = lowestAncestor(head.right, o1, o2);
    // 不可能左边和右边都找到了公共祖先,只可能是左边和右边分别找到了o1、o2,返回根节点
    if(left != null && right != null){
        return head;
    }
    // 左边找不到o1或o2或公共祖先,o1、o2都在右孩子;
    // 右边找不到o1或o2或公共祖先,o1、o2都在左孩子;
    return left != null ? left : right;
}
复制代码

Javascript版

var lowestCommonAncestor = function(root, p, q) {
    if(root == p || root == q || !root) return root;
    let left = lowestCommonAncestor(root.left, p, q);
    let right = lowestCommonAncestor(root.right, p, q);
    if(!left && !right) return null;
    if(left && right){
        return root
    }
    return left || right;
};



相关文章
|
3月前
|
C++ Python
leetcode-235:二叉搜索树的最近公共祖先
leetcode-235:二叉搜索树的最近公共祖先
30 1
|
3月前
|
算法 Java 程序员
【算法训练-二叉树 五】【最近公共祖先】二叉树的最近公共祖先、二叉搜索树的最近公共祖先
【算法训练-二叉树 五】【最近公共祖先】二叉树的最近公共祖先、二叉搜索树的最近公共祖先
52 0
|
3月前
|
存储 算法 前端开发
589. N 叉树的前序遍历
589. N 叉树的前序遍历
21 0
|
3月前
|
Java C++ Python
leetcode-236:二叉树的最近公共祖先
leetcode-236:二叉树的最近公共祖先
22 0
|
8月前
|
C++
二叉树的最近公共祖先(C++实现)
二叉树的最近公共祖先(C++实现)
43 1
LeetCode 235. 二叉搜索树的最近公共祖先
LeetCode 235. 二叉搜索树的最近公共祖先
201 0
LeetCode 235. 二叉搜索树的最近公共祖先
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(上)
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(上)
|
存储 搜索推荐
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(下)
【二叉树OJ题(二)】前序遍历&&中序遍历&&后序遍历&&另一颗树的子树&&二叉树遍历&&平衡二叉树(下)
leetcode235二叉树搜索树的最近公共祖先
leetcode235二叉树搜索树的最近公共祖先
57 0
leetcode235二叉树搜索树的最近公共祖先
|
存储 算法