等效子树

时间:2014-06-05 14:26:33

标签: java algorithm tree graph-theory subtree

我有两棵树。树节点定义为

class Node{
  String treeId; 
  String type;       //Each node has type which has fixed value. For example, its color: RED, BLANK, GREEN
  Set<Node> children;
  String ref;        //The ref is a string and allowed value are "0", "1",..."10". The value is null if it is not leaf. 
};

对于leaf,子集是空的。

我想知道是否有一些现有的有效工作如何识别两个给定树的等效子树。等价物定义为:

1) Both subtree leaves are setsets leaves of original tree. 
2) Both subtrees leaves have same ref value. 
3) for non-leaves node, the equivalent refers to both node have same type and equivalent children. 

感谢。如果有一些Java库解决这个问题会更好。


输入应该是两个树根,而输出是作为等效子树的根的Node。树的高度为100~,它有超过500个节点。


我现在所做的是为Node类添加了一个新字段。

class Cache{
   Map<String, Set<String>> map = new LinkedHashMap<String, Set<Str>>();
}

map的关键是Node id,而value是此nodeid的此节点可以到达的ref set。初始化Node时启动缓存。

在isEquivalent比较阶段,检查两个根的ref set之间是否存在重叠。如果没有,则返回false。

我认为这有助于减少比较空间的数量。

1 个答案:

答案 0 :(得分:0)

我不确定1) Both subtree leaves are leaves of original tree.要求,因为它似乎与how to identify equivalent substree for two given tree.冲突。否则下面的递归方法应该能够涵盖其他两个条件。可以实现haveSameOriginalTree(r1, r2)方法以满足我无法理解的第一个条件。 r1r2是需要检查等效性的两个子树的根。

bool areEquivalent(Node r1, Node r2)
{
    if(r1.children == null && r2.children == null)
    {
        return (haveSameOriginalTree(r1, r2) && (r1.ref == r2.ref));
    }
    if((r1.children == null && r2.children != null) || (r1.children != null && r2.children == null))
    {
        return false;
    }
    // if here then both must be non-leaf nodes
    if(r1.type != r2.type)
    {
        return false;
    }
    if(r1.children.getCount() != r2.children.getCount()) // not sure of correct syntax for Java Sets
    {
        return false;
    }
    for(int i=0; i<r1.children.getCount(); i++)
    {
        if(!areEquivalent(r1.children[i], r2.children[i])) // again please correct the syntax for Sets
        {
            return false;
        }
    }

    return true;
}

让我知道你的想法。

<强>更新

以上是上述解决方案的迭代版本。它使用堆栈数据结构,它在堆上分配而不是推送到函数的调用堆栈,因此与递归没有太大差别,但仍然更好。此外,由于我们只保留对Node的引用(而不是复制整个对象),因此 我们已经将原始树加载到内存中了。

bool areEquivalent(Node r1, Node r2)
{
    Stack<Node> s1 = new Stack<Node>();
    Stack<Node> s2 = new Stack<Node>();
    Node n1, n2;

    s1.Push(r1);
    s2.Push(r2);
    while(true) // Need a better check
    {
        if(s1.getCount() != s2.getCount())
        {
            return false;
        }
        if(s1.getCount() == 0) // if both stacks are empty then we've traversed both trees without failure.
        {
            return true;
        }
        n1 = s1.Pop();
        n2 = s2.Pop();
        if(!areEquivalentNodes(n1, n2))
        {
            return false;
        }
        foreach(Node child in n1.children)
        {
            s1.Push(child);
        }
        foreach(Node child in n2.children)
        {
            s2.Push(child);
        }
    }
}

// only checks the two nodes are equivalent. their childrens' equivalence will be handled by other calls to this method.
bool areEquivalentNodes(Node n1, Node n2)
{
    if(n1.children.getCount() != n2.children.getCount())
    {
        return false;
    }
    if(n1.children.getCount() == 0) // if both are leaf nodes...
    {
        if(n1.ref != n2.ref)
        {
            return false;
        }
    }
    else // both are non-leaf
    {
        if(n1.type != n2.type)
        {
            return false;
        }
        // the condition that children of non-leaf nodes be equivalent will be covered by subsequent calls this method...
    }

    return true;
}

请注意,两个解决方案都需要children个相同顺序的两个等效节点。如果未订购children,则我们需要在调用上述代码之前对其进行排序。

让我知道这是否更好。