树上的高效平等功能

时间:2014-10-15 06:37:08

标签: python algorithm tree time-complexity sml

几天前,我接到了以下面试问题。它是用标准ML代码描述的,但我可以用我选择的语言自由回答(我选择了Python):

  

我有一个类型:

datatype t 
  = Leaf of int
  | Node of (t * t)
     

和一个带有签名

的函数f
val f: int -> t
     

您需要编写一个函数equals来检查是否有两棵树   是平等的。 fO(n),它确实是“最糟糕的   事情“是因为equals函数的时间复杂性。写   equals这样n上的永远不会指数   f

提供的f示例是:

fun f n = 
  if n = 0 then 
    Leaf(0)
  else 
    let 
      val subtree = f (n - 1) 
    in
      Node (subtree, subtree)
    end

O(n)时间内生成一个指数级大的树,因此equals (f(n), f(n))实现的equals实现与树的节点数呈线性关系O(2^n)。< / p>

我制作了这样的东西:

class Node:
    def __init__(self, left, right):
        self.left = left
        self.right = right

class Leaf:
    def __init__(self, value):
        self.value = value

def equals(left, right):
    if left is right:
        return True
    try:
        return left.value == right.value 
    except ValueError:
        pass
    try:
        return equals(left.left, right.left) and equals(left.right, right.right)
    except ValueError:
        return False

以访问者提供的f为例,但在“f的一般情况下失败了”。他提供了一个我不记得的例子,这破坏了我的第一次尝试。我摇摇晃晃了一下,最终做了一些看起来像这样的东西:

cache = {}
def equals(left, right):
    try:
        return cache[(left, right)]
    except KeyError:
        pass

    result = False
    try:
        result = left.value == right.value 
    except ValueError:
        pass
    try:
        left_result = equals(left.left, right.left) 
        right_result = equals(left.right, right.right)
        cache[(left.left, right.left)] = left_result
        cache[(left.right, right.right)] = right_result
        result = left_result and right_result
    except ValueError:
        pass

    cache[(left, right)] = result
    return result

但我觉得这是一个尴尬的黑客,显然不是采访者想要的。我怀疑有一种优雅的方法可以避免重新计算子树 - 这是什么?

2 个答案:

答案 0 :(得分:2)

您可以使用hash consing在线性时间内创建两棵树的副本,然后在恒定时间内将它们进行相等比较。

以下是sml中哈希值的一个例子。

https://github.com/jhckragh/SMLDoc/tree/master/smlnj-lib/HashCons

更新

见评论。我在这个答案中太仓促了。我不认为可以在线性时间内创建副本。你需要从hash-consed类型开始,并且只在f。

中使用那些构造函数

答案 1 :(得分:1)

根据它的外观,你的解决方案是O(n ^ 2)。我们可以通过对单个树的身份进行记忆而不是一对树来使其成为O(n):

memoByVal = {}
memoByRef = {id(None): 0}
nextId = 1

# produce an integer that represents the tree's content
def getTreeId(tree):
  if id(tree) in memoByRef:
    return memoByRef[id(tree)]
  # nodes are represented by the (left, right, value) combination
  # let's assume that leafs just have left == right == None
  l, r = getTreeId(tree.left), getTreeId(tree.right)
  if (l, r, tree.value) not in memoByVal:
    memoByVal[l, r, tree.value] = nextId
    nextId += 1
  res = memoByVal[l, r, tree.value]
  memoByRef[id(tree)] = res
  return res

# this is now trivial
def equals(a, b):
  return getTreeId(a) == getTreeId(b)