我正在编写一些使用树的代码(常规树可以拥有无限数量的节点,但没有交叉,即两个父节点不会指向同一个子节点)。无论如何,有两件事:
1)是否有任何众所周知的算法可以在树中查找子树。
2)是否有任何已实现此算法的Java库(或任何库)?即使没有,任何人都可以推荐任何好的通用Java树库吗?
我想使用这些树来保存树格式的数据,而不是用于搜索功能。
扩展一点:我正在使用树作为游戏的一部分来记录某些事件发生时会发生什么。例如,A可以击中B,可以击中两个A,可以击中另外两个A等。
这看起来像是:
A
|
B
/
A
/ \
A A
/ \
A A
当然,不仅仅是A和B.我想要做的是(对于成就系统)能够告诉他们什么时候,说A已经击中了两个A:
A
/ \
A A
我希望能够轻松地知道第一棵树是否包含该子树。如果我不需要,我不想编写所有代码来执行此操作:)
答案 0 :(得分:6)
看起来像一个简单的算法:在游戏树中找到搜索树的根,并检查搜索树的子项是否是游戏树中子项的子集。
根据你的解释,我不确定搜索树是否
A
/ \
A A
应匹配此树:
A
/|\
A C A
(即如果应该忽略不匹配的孩子。)
无论如何,这是我刚刚玩弄的代码。它是一个完全运行的示例,带有一个main方法和一个简单的Node
类。随意玩吧:
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();
}
}
答案 1 :(得分:2)
您是否正在寻找子树上的任何特定约束?如果没有,simple traversa l应该足以识别子树(基本上将每个节点视为子树的根)。
我相信你会发现你想要的树所需的API因你的特定应用程序而有很大的不同 - 通用实现并不是很有用。也许如果您能告诉我们树将用于哪种应用程序,我们可以提供详细信息。
此外,如果您只是使用树进行数据存储,您可能想问问自己为什么需要树。答案也应该回答我上一段中的问题。
答案 2 :(得分:0)
我想知道Knuth算法的扩展是否比天真的遍历更有效...
答案 3 :(得分:0)
如果有一个大的,静态的树,并且您将在同一个大树中搜索许多子树,您可能希望使用所有子树的哈希值集合到给定深度的每个节点,具体取决于方式你愿意为这个功能花费多少存储空间。然后从哈希值构建一个映射到一组节点,这些节点是具有该哈希值的子树的根。然后检查每一个,可能比遍历要便宜得多,因为查询树根的哈希值达到相同的深度。