leetcode高级算法:二叉树的最近公共祖先

题目如下

给定一个二叉树, 找到该树中两个指定节点的最近公共祖先。

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

例如,给定如下二叉树:  root = [3,5,1,6,2,0,8,null,null,7,4]

        _______3______
       /              \
    ___5__          ___1__
   /      \        /      \
   6      _2       0       8
         /  \
         7   4

示例 1:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1
输出: 3
解释: 节点 5 和节点 1 的最近公共祖先是节点 3。

示例 2:

输入: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4
输出: 5
解释: 节点 5 和节点 4 的最近公共祖先是节点 5。因为根据定义最近公共祖先节点可以为节点本身。

说明:

  • 所有节点的值都是唯一的。
  • p、q 为不同节点且均存在于给定的二叉树中。

这个题已经有相当巧妙的解法(递归判断节点的父子关系),我当时做的时候剑走偏锋想了个别的方法,答案里没有看到这个思路,所以简单记录。

一开始看到这个题的反应是,如果我不想重复地去遍历某些节点,那么在遍历过程中就需要把已经遍历到的内容记录下来以备后查。受到高级算法链表模块中【复制带随机指针地链表】答案的启发,可以用字典来存储已知父子节点对应关系,当然我们是想要从子节点获得父节点的,就可以将子节点、父节点分别记作字典的key和value。当目标节点p、q都遍历到时,我们不关心更深层次节点的构造,可以直接跳出。

        current = [root]
        dic = dict()
        
        while current:
            childs = []
            for node in current:
                if node:
                    left,right = node.left,node.right
                    dic[left] = node
                    dic[right] = node 
                    if dic.get(p) and dic.get(q):
                        break
                    childs.append(left)
                    childs.append(right)
            current = childs

这样的话问题就变成了如何根据这个字典获取公共父节点。从字典中依次回溯,将内容存入列表,找两个列表的第一个公共值当然是可行的一个思路,但是如果树的深度较大,平方复杂度可能造成无法接受的运行时间。其实字典的key-value结构和链表是有些相似的,因此就想到了中级算法中【链表交叉】一题。

编写一个程序,找到两个单链表相交的起始节点。

例如,下面的两个链表

A:          a1 → a2
                   ↘
                     c1 → c2 → c3
                   ↗            
B:     b1 → b2 → b3

在节点 c1 开始相交。

当时解决这个问题的算法是这样的

class Solution(object):
    def getIntersectionNode(self, headA, headB):
        """
        :type head1, head1: ListNode
        :rtype: ListNode
        """
        #两个指针,一个从A遍历到B,一个从B遍历到A,因为A+B和B+A的长度一定是相等的,所以两个指针一定会在相交节点处相遇

        cursor1 = headA
        cursor2 = headB
        
        while cursor1 != cursor2:
            #如果都不相等,第二遍循环到头的时候总会一起返回null
            cursor1 = cursor1.next if cursor1 else headB
            cursor2 = cursor2.next if cursor2 else headA
            
        return cursor1

 这里除了链表换成了字典,其他的问题是一模一样的,代码也可以直接拿过来用,只要把next改为dic即可。完整版代码如下:

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        if not root:
            return None
        if root in [p,q]:
            return root
        
        current = [root]
        dic = dict()
        
        while current:
            childs = []
            for node in current:
                if node:
                    left,right = node.left,node.right
                    dic[left] = node
                    dic[right] = node 
                    if dic.get(p) and dic.get(q):
                        break
                    childs.append(left)
                    childs.append(right)
            current = childs
        
        cursor1 = p
        cursor2 = q
        while cursor1 != cursor2:
            cursor1 = dic[cursor1] if dic.get(cursor1) else q
            cursor2 = dic[cursor2] if dic.get(cursor2) else p            
        
        return cursor1

运行结果:

猜你喜欢

转载自blog.csdn.net/weixin_39655021/article/details/84501635