求给定两个树节点的最低公共祖先

因为树的结构不同所以需要分情况考虑:

  1. 当树为二叉排序树,寻找给定两节点的最低公共祖先

  2. 当树为普通树,每个节点中有指针指向其父节点

  3. 当树为二叉树,每个节点仅有左右孩子指针

  4. 当树为普通树,每个节点仅有左右孩子指针

排序二叉树

排序二叉树相对简单,根据排序二叉树的特点,根节点的左孩子永远小于根节点的值,右孩子的值永远大于根节点的值,对给定节点Node1、Node2,要找其最低公共祖先=>实际上就是寻找一个节点,使得节点的值位于两个节点的值中间,即原问题转化为二叉树的遍历问题

      5   ======排序二叉树
     / \
    3   7
   / \ / \
  2  4 6  9
 /  
1     

树的节点定义如下:

class Node:
    def __init__(self, data):

        self.data = data  # 节点内包含的值
        self.father = None  # 用来标记当前节点的父节点
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子

class SortTree:
    def __init__(self):

        self.root = None

实现寻找父节点的核心代码如下:

        def findParent(self, node_1, node_2, root):

        if root is None:
            return None
        elif root.data>node_1.data and root.data>node_2.data:
            root = self.findParent(node_1, node_2, root.lchild)
        elif root.data<node_1.data and root.data<node_2.data:
            root = self.findParent(node_1, node_2, root.rchild)

        return root

测试代码

def SortTreeParent():
    # 排序二叉树

    # 建树
    n5 = Node(5)
    n3 = Node(3)
    n7 = Node(7)
    n2 = Node(2)
    n4 = Node(4)
    n6 = Node(6)
    n9 = Node(9)
    n1 = Node(1)

    n5.lchild = n3
    n5.rchild = n7

    n3.lchild = n2
    n3.rchild = n4

    n7.lchild = n6
    n7.rchild = n9

    n2.lchild = n1

    tree = SortTree()
    tree.root = n5

    commonFather = tree.findParent(n2, n4, tree.root)

    print(commonFather.data)

如上图排序二叉树,寻找节点2和节点4的公共最低祖先,结果为:
在这里插入图片描述

普通树

普通树的每个节点有一个指向父节点的指针,当要寻找公共父节点,在节点正确给定的情况下,两者最高层的祖先就是根节点,实际上,从Node1,通过父节点指针,可以找到到Node1的所有祖先(最后一个为根节点),同理,也可以找到Node2节点的所有祖先,即原问题转化为求两链表的第一个公共节点(链表不一定等长)

       A    ======普通树(带父节点指针)
      / \
     B   C
   /  \
  D    E
 / \   / \
F   G H   I  
class Node:
    def __init__(self, data):

        self.data = data  # 节点内包含的值
        self.father = None  # 用来标记当前节点的父节点
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子

class NormalTree:

    def __init__(self):
        self.root = None

寻找最低公共父节点核心代码:

# 寻找两个链表的第一个公共节点
    def findParent(self, node_1, node_2):

        # 节点不为空
        if node_1 is None or node_2 is None:
            return None

        # 得到两链表的长度
        len1 = 1
        temp = node_1
        while(temp.father is not None):
            len1 += 1
            temp = temp.father

        len2 = 1
        temp = node_2
        while(temp.father is not None):
            len2 += 1
            temp = temp.father

        stap = int(abs(len1 - len2))

        tempLong = node_1
        tempShort = node_2
        if len1 >= len2:
            tempLong = node_1
            tempShort = node_2
        elif len1 < len2:
            tempLong = node_2
            tempShort = node_1

        for i in range(stap):
            tempLong = tempLong.father

        for i in range(min(len1, len2)):
            if tempLong.data == tempShort.data:
                return tempShort

            tempShort = tempShort.father
            tempLong = tempLong.father
I
        return None

测试代码

def NormalTreeParent():
    na = Node("A")
    nb = Node("B")
    nc = Node("C")
    nd = Node("D")
    ne = Node("E")
    nf = Node("F")
    ng = Node("G")
    nh = Node("H")
    ni = Node("I")

    na.lchild = nb
    na.rchild = nc

    nb.lchild = nd
    nb.rchild = ne
    nb.father = na

    nc.father = na

    nd.lchild = nf
    nd.rchild = ng
    nd.father = nb

    ne.lchild = nh
    ne.rchild = ni
    ne.father = nb

    nf.father = nd
    ng.father = nd

    nh.father = ne
    ni.father = ne

    tree = NormalTree()
    tree.root = na

    commonFather = tree.findParent(nf, nb)
    print(commonFather.data)

实例:如上图所示,寻找节点D和节点I的最低公共祖先,结果如下:
在这里插入图片描述

普通二叉树(无指向父节点的指针)

当无指向父节点的指针时,要想找到最低公共祖先,可结合普通树的思维方式,考虑从根节点root开始,找出root到Node1和Node2的路径,通过路径找最低公共祖先,即原问题转化为寻找二叉树中根节点到指定节点的路径(需要利用回溯的思想)+寻找链表最后一个公共节点的问题。

      A    ======普通二叉树
     / \
    B   C
   /  \
  D    E
 / \  / \
F   G H  I

树节点的定义:

class Node:
    def __init__(self, data):

        self.data = data  # 节点内包含的值
        self.lchild = None  # 左孩子
        self.rchild = None  # 右孩子


class GenericTree:
    """普通的二叉树(没有父节点)

    """
    def __init__(self):

        self.root = None

寻找的过程主要分为两步:先找出路径,再求路径上最后一个公共节点,代码如下:

    def findPath(self, root, node, path):
        # 回溯方法寻找根节点到某节点的路径
        if root is None or node is None:
            return False

        if root.data == node.data:
            path.append(root)
            return True

        # 递归左右孩子节点
        if self.findPath(root.lchild, node, path):
            path.append(root)
            return True
        if self.findPath(root.rchild, node, path):
            path.append(root)
            return True

        return False
    def findParent(self, node_1, node_2):
    	# 路径是从目标节点到根节点
        path1 = []
        self.findPath(self.root, node_1, path1)
        path2 = []
        self.findPath(self.root, node_2, path2)

        len1 = len(path1)
        len2 = len(path2)

        if len1 > len2:
            path1 = path1[len1-len2:]
        else:
            path2 = path2[len2-len1:]

        for i in range(min(len1, len2)):
            if path1[i] == path2[i]:
                # 因为至少会有一个根节点是共同的祖先,所以在循环内return就可以,无需另外判断
                return path2[i]

测试代码

def GenericTreeParent():
    na = Node("A")
    nb = Node("B")
    nc = Node("C")
    nd = Node("D")
    ne = Node("E")
    nf = Node("F")
    ng = Node("G")
    nh = Node("H")
    ni = Node("I")


    na.lchild = nb
    na.rchild = nc

    nb.lchild = nd
    nb.rchild = ne

    nd.lchild = nf
    nd.rchild = ng

    ne.lchild = nh
    ne.rchild = ni

    tree = GenericTree()
    tree.root = na
    commonFather = tree.findParent(nh, nc)
    print(commonFather.data)

实例:寻找节点H到节点C的最低公共祖先,运行结果如下:
在这里插入图片描述

普通树

对二叉树的操作,总是仅涉及最多左右孩子两个指针的问题,然而对于具有普通结构的树,每个节点的孩子节点数不同,我们就需要改变树节点的定义,可以考虑用一个结合来存储树的孩子节点的信息,思路相对不变,仅在查找路径时有些许改变。

        A    ======普通树(不带父节点指针)
      / | \
     B  C  K
   /   \
  D     E
 / \   /|\
F   G H I J

树的定义:

class TreeNode:
    def __init__(self, data):
        self.data = data
        self.children = []


class Tree:
    def __init__(self):
        self.root = None

核心代码

    def findPath(self, root, node, path):
        # 回溯方法寻找根节点到某节点的路径
        if root is None or node is None:
            return False

        if root.data == node.data:
            path.append(root)
            return True

        # 递归所有孩子节点
        for child in root.children:
            if self.findPath(child, node, path):
                path.append(root)
                return True

        return False
    def findParent(self, node_1, node_2):
        path1 = []
        self.findPath(self.root, node_1, path1)
        path2 = []
        self.findPath(self.root, node_2, path2)

        len1 = len(path1)
        len2 = len(path2)

        if len1 > len2:
            path1 = path1[len1-len2:]
        else:
            path2 = path2[len2-len1:]

        for i in range(min(len1, len2)):
            if path1[i] == path2[i]:
                # 因为至少会有一个根节点是共同的祖先,所以在循环内return就可以,无需另外判断
                return path2[i]

测试代码

def TreeParent():
    na = TreeNode("A")
    nb = TreeNode("B")
    nc = TreeNode("C")
    nd = TreeNode("D")
    ne = TreeNode("E")
    nf = TreeNode("F")
    ng = TreeNode("G")
    nh = TreeNode("H")
    ni = TreeNode("I")
    nj = TreeNode("J")
    nk = TreeNode("K")

    na.children.append(nb)
    na.children.append(nc)
    na.children.append(nk)

    nb.children.append(nd)
    nb.children.append(ne)

    nd.children.append(nf)
    nd.children.append(ng)
    ne.children.append(nh)
    ne.children.append(ni)
    ne.children.append(nj)

    tree = Tree()
    tree.root = na
    commonFather = tree.findParent(ng, nj)
    print(commonFather.data)

实例:寻找节点G与节点J的最低公共祖先,运行结果如下:
在这里插入图片描述
害,关于树的问题实在是太多了,但是分解成一个个的小问题就不显得那么难了,递归在树中的应用实在太多了,最低公共祖先就算是更新完了,完整代码链接

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值