【问题标题】:Binary tree: first common ancestor二叉树:第一个共同祖先
【发布时间】:2018-02-02 18:58:15
【问题描述】:

在过去的几天里,我一直在与树和蟒蛇作斗争。大多数情况下,树中的递归给我带来了麻烦。我要解决的问题是在 二叉树 中找到第一个共同祖先。有很多解决方案声称已经做到了这一点,但它们都是针对二叉搜索树的,而不是二叉树。在二叉树的情况下,节点没有排序,所以左边小于右边。我知道我应该使用哪种方法,但我在递归部分失败了:(编辑:问题表明我不能使用额外的数据结构或存储)

class Node:

    """docstring for Node"""
    def __init__(self, data):
        self.data = data
        self.left=None
        self.right=None

def findNode(self,target):
    if self==None:
        return 0
    if self.data==target:
        return 1
    return self.findNode(self.left,target) or self.findNode(self.right,target)

def firstCommonAncestor(self,p,q):
    if self==None:
        return 0
    if self.left.data==p and self.right.data==q:
        return self.data
    if findNode(self.left,p) and findNode(self.right,q):
       return 1

root=Node(2)
root.left=Node(5)
root.right=Node(4)
root.left.left=Node(9)
root.left.right=Node(7)
print firstCommonAncestor(root,9,7)

我编辑了代码以使问题更清楚。 findNode(self.left,p) 和 findNode(self.right,q) 应该返回 1,因为两个节点都存在。但是,当 findNode(self.right,q) 不是从根开始搜索时。我知道我应该实现递归调用,但我尝试过的一切都失败了。如果有人可以提供一些关于我做错了什么的指示,将不胜感激! (第一个CommonAncestor 还没有实现,所以这并不重要。它现在没有做太多)。编辑:这是破解编码面试的问题。

【问题讨论】:

    标签: python recursion tree binary-tree


    【解决方案1】:

    (只是给你一个提示为什么它不工作)

    当您搜索 y 时,它不会回到根目录。您的代码正在做正确的事情。 找不到Node(7)的原因是因为你的数据。

    这是你的树。

             2
             |
          -------
         5       4
      -------
      9     7 
    

    您的 x 搜索是 findNode(Node(5), 9) 找到 9。

    虽然您的 y 搜索是 findNode(Node(4), 7),但它当然永远找不到 7

    希望对您有所帮助。

    【讨论】:

      【解决方案2】:

      另一个提示:您的实例方法从类中消失了,并且只是普通的全局方法(这不仅仅是缩进的问题,因为您也以错误的方式调用它们)。这是findNode的正确定义:

      class Node:
      
          """docstring for Node"""
          def __init__(self, data):
              self.data = data
              self.left=None
              self.right=None
      
          def findNode(self,target):
              result = None
      
              if self.data == target:
                  return self
      
              result = self.left.findNode(target) if self.left else None
              if not result:
                  result = self.right.findNode(target) if self.right else None
      
              return result
      
          def firstCommonAncestor(self, p, q):
              pass #TODO
      

      findNode 方法中,您还有一个如何调用它的示例。您还应该在 firstCommonAncestor 方法中解决此问题。

      【讨论】:

        【解决方案3】:
        class Node:
            def __init__(self, data):
                self.data = data
                self.left = None
                self.right = None
        
            def isAncestor(self, p, q):
                ret = 0
                if self.data == None:
                    return ret
                if (self.data == p):
                    ret += 1
                if self.data == q:
                    ret += 1
                if ret == 2:
                    return ret
                if self.left!=None:
                    ret += self.left.isAncestor(p, q)
                if ret == 2:
                    return ret
                if self.right!=None:
                    return ret + self.right.isAncestor(p ,q)
                return ret
        
            def commonAncestor(self, p, q):
                if q == p and (self.left.data == q or self.right.data == q):
                    return self
                lNodes = self.left.isAncestor(p, q)
                if lNodes == 2:
                    if self.left.data == p or self.left.data == q:
                        return self.left
                    else:
                        return self.left.commonAncestor(p, q)
                elif lNodes == 1:
                    if self.data == p:
                        return p
                    elif self.data == q:
                        return q
        
                rNodes = self.right.isAncestor(p, q)
                if rNodes == 2:
                    if self.right.data == p or self.right.data == q:
                        return self.right
                    else:
                        return self.right.commonAncestor(p, q)
                elif rNodes == 1:
                    if self.data == p:
                        return p
                    elif self.data == q:
                        return q
        
                if lNodes == 1 and rNodes ==1:
                    return self
                else:
                    return None
        
        
        """
                     2
                   /   \
                  5     4
                /  \   /  \
               9    7     11
                            \
                            12
        """
        if __name__ == '__main__':
            root=Node(2)
            root.left=Node(2)
            root.right=Node(4)
            root.right.right=Node(11)
            root.left.left=Node(9)
            root.left.right=Node(7)
            root.right.right.right=Node(12)
            common = root.commonAncestor(2,2)
            if common != None:
                print common.data
            else:
                print "Not found"
        

        【讨论】:

          【解决方案4】:

          由于树没有排序,因此无论如何您都必须搜索很多内容。而且由于不允许您使用额外的数据结构,因此您有重复大量搜索的危险。

          所以递归到叶节点一次,然后返回组合数据可能是最有效的。这是 O(n),但单个搜索也是如此。

          这就是下面的代码尝试做的事情。搜索方法返回(a's parent, b's parent),如果它们不同,但都设置了,那么我们就是共同的祖先。

          def search(self, a, b):
              ap1 = ap2 = ap3 = bp1 = bp2 = bp3 = None
              # parents to left
              if self.left:
                  ap1, bp1 = self.left.search(a, b)
              # parents to right
              if self.right:
                  ap2, bp2 = self.right.search(a, b)
              # are we an immediate "parent" ourselves?
              if self.data == a: 
                  ap3 = self
              elif self.data == b:
                  bp3 = self
              # only one of the above can succeed, so find it
              ap = ap1 or ap2 or ap3
              bp = bp1 or bp2 or bp3
              # if we are the point where two paths meet at the common
              # ancestor, return ourselves
              if ap and bp and ap != bp:
                  return self, self
              # otherwise, return what we have
              else:
                  return ap, bp
          

          【讨论】:

            【解决方案5】:

            编辑:重新设计的解决方案使其更清洁并解决来自 cmets 的问题

            有相当有效的解决方案,但有点复杂。它涉及钻入树并跟踪您是否在返回时找到了第一个或第二个值。如果在某个时候您发现(第一个和第二个)都返回该节点,它将成为您的共同祖先。

            这是更有效的解决方案,但如果你有重复,它就不起作用,但它可以帮助你获得想法并解决重复的情况:

            class Node:
                """docstring for Node"""
                def __init__(self, data):
                    self.data = data
                    self.left=None
                    self.right=None
            
                def union(self, u1, u2):
                    res = u1[0] or u2[0], u1[1] or u2[1], u1[2] or u2[2]
                    if res[0] and res[1] and not res[2]:
                        return res[0], res[1], self
                    return res
            
                def doCommon(self, p, q):
                    # recursion base case
                    l = (False, False, None)
                    r = (False, False, None)
                    if self.left:
                        l = self.left.doCommon(p, q)
                    if self.right:
                        r = self.right.doCommon(p, q)
            
                    res = self.union(l, r)
                    if res[0] and res[1]:
                        return res
            
                    if self.data == p:
                        return self.union((True, False, None), res)
                    if self.data == q:
                        return self.union((False, True, None), res)
                    return res
            
                def common(self, p, q):
                    return self.doCommon(p, q)[2]
            
            
            
            if __name__ == '__main__':
                root=Node(2)
                root.left=Node(5)
                root.right=Node(4)
                root.left.left=Node(9)
                root.left.right=Node(7)
                res = root.common(9,7)
                if res != None:
                    print res.data
                else:
                    print "Not found"
            

            【讨论】:

            • 我明白这一点。该问题特别指出要避免额外的数据结构或创建额外的存储。我编辑了原始问题以澄清这一点。
            • 是的,图是一种更通用的数据结构,需要更通用的算法。
            • 谢谢你,我明白我的错误在哪里了!
            • 小心,这不是最有效的解决方案。影响更大的是有一个标志来说明两个值是否都存在,否则您必须在每个递归调用中检查它。
            • 我知道这是低效的。我的问题是代码内部的错误:我将函数放在节点类之外,因此,很多事情都出错了。
            猜你喜欢
            • 1970-01-01
            • 2014-12-11
            • 2011-07-28
            • 2012-11-08
            • 1970-01-01
            • 1970-01-01
            • 2011-09-04
            • 2012-08-16
            • 2012-01-16
            相关资源
            最近更新 更多