查找匹配Java中给定子树的树中的所有子树

时间:2010-01-20 14:16:13

标签: java tree matching subtree isomorphism

我在Java中编写使用无序的根树的代码,其中每个节点可以有任意数量的子节点。给定树T和子树S,我希望能够找到T中与S匹配的所有子树(即T中与S同构的所有子树)。

如果S的节点可以映射到T的节点,使得S的边缘映射到T中的边缘,则T的子树与S是同构的。

已经询问previous question如何查找树是否包含另一个子树但是我希望能够在T中找到匹配S的 ALL 子树。另外我想要能够将T中每个匹配中的每个节点映射到S中的相应节点。

也就是说,当找到匹配时,它应该不仅仅作为指向T中节点的指针返回,其中树的根与S匹配,但匹配应该像对象列表一样返回节点[(T1,S1),(T2,S2),...(Tn,Sn)]使得T1是指向T中节点的指针,该节点映射到子树中的节点S1,依此类推。

或者,只需返回一对值列表,因为树T中的每个节点和子树S都有一个与之关联的唯一整数标识符。

例如:

给出树T如下:

    a
   / \
  b   c
 / \  
d   e

和子树S为:

    x
   / \
  y   z

应返回以下匹配列表:

  

[(A,X),(B,Y),(C,Z)]   [(B,X),(d,Y),(E,Z)]

唯一匹配由T中的节点集决定, T和S中节点之间的映射。

以下匹配:

  

[(A,X),(B,以Z 的),(C,ý)]

被认为是

的副本
  

[(A,X),(B,的ý),(C,的ž)]

因为它们具有来自T(a,b,c)的相同节点集,所以只应返回其中一个匹配。

另一个例子,给定树T:

    a
   /|\
  b c d

和子树S:

  x
 / \  
y   z

应返回以下匹配列表:

  

[(A,X),(B,Y),(C,Z)]   [(A,X),(B,Y),(d,Z)〕   [(A,X),(C,Y),(d,Z)〕

任何人都可以提供如何执行此操作的示例代码吗?

编辑(与Chris Kannon的评论有关):

  

我想你想要有人代码   答案给你?你有多远   得到?你写了什么代码? -   Chris Kannon 1小时前

我有以下代码,在运行时,构建一个指向树中节点的指针列表(matchesList),其中子树根与给定的子树相匹配。但是,可能存在多个以同一节点为根的子树,并且当前每个节点最多只会添加一次到matchesList,而不管有多少匹配在那里。

另外,我无法弄清楚如何在子树中的节点和原始树中找到的匹配节点之间建立上述映射。

package Example;

import java.util.LinkedList;
import java.util.Vector;

public class PartialTreeMatch {
    public static void main(String[] args) {
        NodeX testTree = createTestTree();
        NodeX searchTree = createSearchTree();

        System.out.println(testTree);
        System.out.println(searchTree);

        partialMatch(testTree, searchTree);
    }

    static LinkedList<NodeX> matchesList = new LinkedList<NodeX>();

    private static boolean partialMatch(NodeX tree, NodeX searchTree) {
        findSubTreeInTree(tree, searchTree);
        System.out.println(matchesList.size());
        for (NodeX n : matchesList) {
            if (n != null) {
                System.out.println("Found: " + n);
            }
        }

        return false;
    }

    private static NodeX findSubTreeInTree(NodeX tree, NodeX node) {
        if (tree.value == node.value) {
            if (matchChildren(tree, node)) {
                matchesList.add(tree);

            }
        }

        NodeX result = null;
        for (NodeX child : tree.children) {
            result = findSubTreeInTree(child, node);

            if (result != null) {
                if (matchChildren(tree, result)) {
                    matchesList.add(result);

                }
            }
        }

        return result;
    }

    private static boolean matchChildren(NodeX tree, NodeX searchTree) {
        if (tree.value != searchTree.value) {
            return false;
        }

        if (tree.children.size() < searchTree.children.size()) {
            return false;
        }

        boolean result = true;
        int treeChildrenIndex = 0;

        for (int searchChildrenIndex = 0; searchChildrenIndex < searchTree.children
                .size(); searchChildrenIndex++) {

            // Skip non-matching children in the tree.
            while (treeChildrenIndex < tree.children.size()
                    && !(result = matchChildren(tree.children
                            .get(treeChildrenIndex), searchTree.children
                            .get(searchChildrenIndex)))) {
                treeChildrenIndex++;
            }

            if (!result) {
                return result;
            }
        }

        return result;
    }

    private static NodeX createTestTree() {

        NodeX subTree2 = new NodeX('A');
        subTree2.children.add(new NodeX('A'));
        subTree2.children.add(new NodeX('A'));

        NodeX subTree = new NodeX('A');
        subTree.children.add(new NodeX('A'));
        subTree.children.add(new NodeX('A'));
        subTree.children.add(subTree2);

        return subTree;
    }

    private static NodeX createSearchTree() {
        NodeX root = new NodeX('A');
        root.children.add(new NodeX('A'));
        root.children.add(new NodeX('A'));

        return root;
    }
}

class NodeX {
    char value;
    Vector<NodeX> children;

    public NodeX(char val) {
        value = val;
        children = new Vector<NodeX>();
    }

    public String toString() {
        StringBuilder sb = new StringBuilder();

        sb.append('(');
        sb.append(value);

        for (NodeX child : children) {
            sb.append(' ');
            sb.append(child.toString());
        }

        sb.append(')');

        return sb.toString();
    }
}

上面的代码试图找到所有子图:

  A
 /|\  
A A A
   / \
  A   A

匹配:

    A
   / \
  A   A

代码成功检测到根据第一个树中的顶部节点和第一个树的第3个子节点的匹配。但是,实际上有3个匹配在顶层节点,而不仅仅是一个。此外,代码不会在树中的节点和子树中的节点之间建立映射,我无法弄清楚如何执行此操作。

有人可以就如何做到这一点提出任何建议吗?

2 个答案:

答案 0 :(得分:5)

我认为你的递归方法需要返回一个部分匹配列表,而不仅仅是一个布尔值。这将解决你的问题(需要返回匹配列表,以及找到多个匹配)。

递归函数的类Java伪代码可能如下所示:

findMatches(treeNode, searchNode) {
    if searchNode has no children {
        // search successful
        pairs = []  // empty list
        return [pairs]  // list of lists
    }
    else {
        matches = []  // empty list
        searchChild = first child node of searchNode
        searchNode2 = searchNode with searchChild removed
        // NOTE: searchNode2 is created by doing a shallow copy of just the node
        // (not it's children) and then removing searchChild from the child list.

        for each treeChild in treeNode.children {
            if treeChild.value == searchChild.value {
                treeNode2 = treeNode with treeChild removed  // also a shallow copy
                childMatches = findMatches(searchChild, treeChild)
                nodeMatches = findMatches(treeNode2, searchNode2)

                // cross-product
                for each nodeMatchPairs in nodeMatches {
                    for each childMatchPairs in childMatches {
                        fullMatchPairs = [(searchChild, treeChild)]
                            + childMatchPairs + nodeMatchPairs  // concatenate lists
                        add fullMatchPairs to matches
                    }
                }
            }
        }

        return matches
    }
}

请注意,此函数不测试treeNode.value == searchNode.value,或将其添加到列表中。来电者需要这样做。此函数需要在树的每个节点上运行。

按照目前的设计,它可能会占用太多内存,但可以进行优化。

答案 1 :(得分:2)

This可能会有所帮助。