简体   繁体   English

在树中查找子树的简单方法

[英]Easy way to find Subtree in a Tree

I'm writing some code that uses a Tree (a regular tree that can have an unlimited number of nodes, but no crossover, ie two parent nodes will not point the the same child node).我正在编写一些使用树的代码(可以有无限数量的节点但没有交叉的常规树,即两个父节点不会指向同一个子节点)。 Anyway, two things:总之,有两点:

1) Are there any well-known algorithms for finding a sub-tree within a tree. 1)是否有任何众所周知的算法可以在树中找到子树。

2) Are there any Java libraries (or any libraries for that matter) that already implement this algorithm? 2)是否有任何已经实现此算法的 Java 库(或任何库)? Even if there are none, can anyone recommend any good general purpose Java tree library?即使没有,任何人都可以推荐任何好的通用Java树库吗?

I want to use these trees for holding data in a tree format, not for their searching capabilities.我想使用这些树来保存树格式的数据,而不是它们的搜索功能。

To expand a bit: I'm using the tree as part of game to keep a history of what happens when a certain events happen.扩展一点:我将树用作游戏的一部分,以记录特定事件发生时所发生的情况。 For example, an A can hit a B which can hit two A's which can hit another two A's etc.例如,一个 A 可以击中一个 B,该 B 可以击中两个 A,而该 B 可以击中另外两个 A,等等。

That would look something like:这看起来像:

    A
    |
    B
   /
  A 
 / \  
A   A
   / \
  A   A

Of course there's more than just A and B. What I want to do is (for an achievement system) is be able to tell when, say an A has hit two A's:当然,不仅仅是 A 和 B。我想要做的是(对于成就系统)能够判断什么时候,比如说一个 A 达到了两个 A:

  A
 / \
A   A

I want to be able to easily know if the first tree contains that subtree.我希望能够轻松知道第一棵树是否包含该子树。 And I don't want to have to write all the code for doing so if I don't have to:)如果我不需要的话,我不想写所有的代码来这样做:)

Looks like a straightforward algorithm: Find the root of the search tree in the game tree and check whether the children of the search tree are a subset of the children in the game tree.看起来很简单的算法:在博弈树中找到搜索树的根,并检查搜索树的子节点是否是博弈树中子节点的子集。

From your explanations, I'm not sure whether the search tree根据您的解释,我不确定搜索树是否

  A
 / \
A   A

should match this tree:应该匹配这棵树:

  A
 /|\
A C A

(ie if non-matching children are supposed to be ignored.) (即如果不匹配的孩子应该被忽略。)

Anyway, here's the code I just toyed around with.无论如何,这是我刚刚玩弄的代码。 It's a fully running example and comes with a main method and a simple Node class.这是一个完全运行的示例,带有一个主要方法和一个简单的Node class。 Feel free to play with it:随意玩它:

import java.util.Vector;

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

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

        partialMatch(testTree, searchTree);
    }

    private static boolean partialMatch(Node tree, Node searchTree) {
        Node subTree = findSubTreeInTree(tree, searchTree);
        if (subTree != null) {
            System.out.println("Found: " + subTree);
            return true;
        }
        return false;
    }

    private static Node findSubTreeInTree(Node tree, Node node) {
        if (tree.value == node.value) {
            if (matchChildren(tree, node)) {
                return tree;
            }
        }

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

            if (result != null) {
                if (matchChildren(tree, result)) {
                    return result;
                }
            }
        }

        return result;
    }

    private static boolean matchChildren(Node tree, Node 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 Node createTestTree() {
        Node subTree1 = new Node('A');
        subTree1.children.add(new Node('A'));
        subTree1.children.add(new Node('A'));

        Node subTree2 = new Node('A');
        subTree2.children.add(new Node('A'));
        subTree2.children.add(new Node('C'));
        subTree2.children.add(subTree1);

        Node subTree3 = new Node('B');
        subTree3.children.add(subTree2);

        Node root = new Node('A');
        root.children.add(subTree3);

        return root;
    }

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

        return root;
    }
}

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

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

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

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

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

        sb.append(')');

        return sb.toString();
    }
}

Are you looking for any particular constraints on a subtree?您是否正在寻找对子树的任何特定约束? If not, a simple traversa l should suffice for identifying subtrees (basically treat each node as the root of a subtree).如果不是,一个简单的遍历应该足以识别子树(基本上将每个节点视为子树的根)。

I believe you'll find that the API you'll want for a tree varies a great deal by your particular application -- so much that generic implementations are not very useful.我相信您会发现,您需要的 API 因您的特定应用程序而异,以至于通用实现不是很有用。 Perhaps if you could tell us what kind of application the tree will be used for, we could provide particulars.也许如果你能告诉我们这棵树将用于什么样的应用程序,我们可以提供细节。

Also, if you're just using a tree for data storage, you might want to ask yourself why you need a tree.此外,如果您只是使用树来存储数据,您可能想问自己为什么需要树。 That answer should also answer the question in my previous paragraph.这个答案也应该回答我上一段中的问题。

If there is one big, static, tree and you will be searching for many subtrees in the same big tree, you might want to annotate each node with the set of hashes of all of its subtrees to a given depth depending on how much storage you're willing to expend on that functionality.如果有一个大的 static 树,并且您将在同一棵大树中搜索许多子树,您可能希望使用其所有子树的哈希集注释每个节点到给定的深度,具体取决于您的存储量'愿意花费在那个功能上。 Then build a map from hash values to the set of nodes that are roots of a subtree with that hash value.然后构建一个 map,从 hash 值到作为具有该 hash 值的子树的根的节点集。 Then just check each one of those, presumably much cheaper than a traversal, for the hash of the root of the query tree to that same depth.然后只需检查每一个,可能比遍历便宜得多,查询树的根的 hash 到相同的深度。

I wonder if there's an extension of the Knuth algorithm that would be more efficient than a naive traversal...我想知道 Knuth 算法的扩展是否比简单的遍历更有效......

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM