Effective Equality Function on Trees

A few days ago I was given the following interview question. It was described with standard ML code, but I was free to answer in the language of my choice (I chose Python):

I have a type:

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

and function fwith signature

val f: int -> t

You need to write a function equalsthat checks if two trees are equal. fmakes up O(n), and this is the “worst possible thing” for the time complexity of your function equals. equalssuch that it is never exponential on n, argument f.

An example fthat was provided was:

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).

- :

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

, , , . , - ?

+4
2

O (n ^ 2) . O (n), memoization , :

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

# produce an integer that represents the tree content
def getTreeId(tree):
  if id(tree) in memoByRef:
    return memoByRef[id(tree)]
  # nodes are represented by the (left, right, value) combination
  # let 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)
+1

All Articles