• 求解二叉树中两个结点的最低公共父结点


    一,问题描述

    构建一棵二叉树(不一定是二叉查找树),求出该二叉树中某两个结点的最低公共父结点。借用一张图如下:

    Binary Tree

    结点8 和 结点5 的最低公共父结点为 结点2

    二,二叉树的构建

    与 求二叉树中第K层结点的个数 文章中的第二点:二叉树构建相同

    三,求解最低公共父结点的算法实现

    有两种思路,一种是通过中序遍历和后序遍历。由于中序遍历是先左子树中的结点,再访问根,再访问右子树中结点,因此这两个结点的公共父结点一定处于这两个结点之间。

    如:中序遍历:8, 4, 9, 2, 5, 1, 6, 3, 7     结点2处于结点8 和 结点5 之间,也就是说:结点8 和 结点5 的最低公共父结点在 [8~5]之间的候选结点,这里为{4,9,2}中取

    后序遍历是先访问左右子树中的结点,最后再访问根。故这两个结点的最低公共父结点一定处于 结点8 和 结点5 之后的结点,且是第一个出现在{4,9,2}中的那个结点。

    后序遍历:8, 9, 4, 5, 2, 6, 7, 3, 1        8->9->4->5 这之后的结点,才可能是 结点8 和 结点5 的父结点。

    另一种方法则是:递归,首先从树根开始考虑:

    ①结点A 和 结点B 要么都在树根的左子树中;②要么都在树根的右子树中;③要么一个在左子树中,一个在右子树中。

    这是一个分治算法,对于情况①和②,可以继续递归分解。对于情况③属于代码第10行判断,复杂度为O(1)

    递归表达式可表示为:T(N)=2T(N/2)+O(1),解得T(N)=O(N)

    对于③,最低公共父结点为树根。

    对于①,可以进一步判断,从树根的左孩子结点考虑:

    1)结点A 和 结点B 要么都在树根的左子孩子 的 左子树中;2)要么都在树根的左孩子 的 右子树中;3) 要么一个在树根的左孩子的 左子树中,一个在树根的左孩子 的 右子树中。

    对于②,可以进一步判断,从树根的右孩子的结点考虑:

    1)结点A 和 结点B 要么都在树根的右子孩子 的 左子树中;2)要么都在树根的右孩子 的 右子树中;3) 要么一个在树根的右孩子的 左子树中,一个在树根的右孩子 的 右子树中。

    下面代码实现递归求解最低公共父结点。

    四,代码实现(node1 node2 都是二叉树中的结点)

     1     /**
     2      * 求解node1 和 node2 的最低公共父结点
     3      * @param node1
     4      * @param node2
     5      * @return 最低公共父结点
     6      */
     7     public BinaryNode<T> commonNode(BinaryNode<T> node1, BinaryNode<T> node2, BinaryNode<T> root){
     8         if(root == null)
     9             return null;
    10         if(node1.element == root.element || node2.element == root.element)
    11             return root;
    12         /*
    13          * 若 left==null, node1,node2 都不在 root.left子树中
    14          * 若right==null,node1,node2 都不在root.right子树中
    15          */
    16         BinaryNode<T> left = commonNode(node1, node2, root.left);
    17         BinaryNode<T> right = commonNode(node1, node2, root.right);
    18         
    19         if(left != null && right != null)
    20             return root;
    21         return left == null ? right : left;
    22     }

    根据程序中的第8行和第10行的if语句,可知:

    1)若 left==null, 则说明 node1,node2 都不在 root.left子树中

    2)若right==null,则说明 node1,node2 都不在root.right子树中

    3)当对于某个结点,当执行了第16,17行的递归后 ,left 和 right 都不为空,说明node1 在 该结点.left子树中,node2 在 该结点.right子树中

    故第19-20行,返回 该结点 作为公共父结点

    例如,求结点8 和 结点5 的最低公共父结点:递归调用过程如下:

    a)commNode(8,5,1)==2

    b1)      commNode(8,5,2)==2

    c1)             commNode(8,5,4)==8

    d1)                      commNode(8,5,8)==8

    d2)                      commNode(8,5,9)==null

    c2)             commNode(8,5,5)==5

    b2)      commNode(8,5,3)==null

    a)生成了 b1)  b2) 两个递归调用,其中 b2)为空,因为结点8,结点5 不在以3为根的子树中

    b1)生成了 c1)   c2)两个递归调用,其中 c2) 是commNode(8,5,5),根据程序第10行if,返回5,而c1)又生成了 d1)  d2)两个递归调用

    其中,d1) 返回8,d2)返回null, 故执行到第21行语句,return 不空的那个left/right,也就是 d1) 返回的结点8

    由于 c1)返回了5, c2)返回了8,都不为空,执行到第19-20行,返回它们的root,即,结点4和结点5的公共父结点:结点2

    由于 b1) 生成了 c1)  c2)   故 b1) 的值是结点2    又因为 b2) 为null

    故 a) 最终是 b1) 的值,即为结点2

    可把该方法放到求二叉树中第K层结点的个数 中 的完整代码给出的程序中进行测试。

    五,参考资料

    How to find the lowest common ancestor of two nodes in any binary tree?

    附:求解二叉查找树的最低公共祖先结点:https://www.cnblogs.com/hapjin/p/5770596.html

  • 相关阅读:
    06 is和==的区别 encode()编码 decode()解码
    05 dic的增删改查 字典的嵌套 考试题dic.get()的相关使用
    03 编码 int ,bool,str的常用操作 主要讲str
    01 基本数据类型 变量 if语句
    04 列表的增删改查 常用方法 元祖 range
    02 while循环 格式化输出 运算符
    多校2 Harmonious Army hdu6598 网络流
    P3159 [CQOI2012]交换棋子 网络流
    P2172 [国家集训队]部落战争 最大流
    P2402 奶牛隐藏 网络流
  • 原文地址:https://www.cnblogs.com/hapjin/p/5508284.html
Copyright © 2020-2023  润新知