В последнее время я сталкиваюсь с проблемой алгоритма: дерево определяется как
public class Node
{
int id;
private final List<Node> children;
Node(int id) {
this.id = id;
this.children = new ArrayList<>();
}
}
Два поддеревья являются общими, если их структура идентична. Самые большие общие поддеревья максимизируют количество узлов в каждом отдельном поддереве. Итак, как найти общий поддерево maxmum (идентификатор каждого узла не имеет значения, только структура поддеревьев будет одинаковой). Если существуют отдельные группы поддеревьев, которые имеют общий размер с одинаковым максимальным размером, нам следует вернуть корневые узлы из всех поддеревьев.
Моя идея состоит в сериализации каждого поддерева в уникальную строку с использованием BFS. После того, как мы получим все строки, сортируем их и сравниваем, какие два равны. Итак, ниже мой код. Мой вопрос заключается в том, что сериализация каждого поддерева вызывает много накладных расходов, есть ли другая идея решить эту проблему в более сложной временной сложности.
public static List<Node> getLargestCommonSubtrees(Node root) {
HashMap<String, ArrayList<Node>> map = new HashMap<String, ArrayList<Node>>();
LinkedList<Node> queue = new LinkedList<Node>();
queue.add(root);
while (!queue.isEmpty()) {
Node cur = queue.pollFirst();
String sig = serialize(cur);
if (map.containsKey(sig)) {
ArrayList<Node> list = map.get(sig);
list.add(cur);
map.put(sig, list);
} else {
ArrayList<Node> list = new ArrayList<Node>();
list.add(cur);
map.put(sig, list);
}
for (int i = 0; i < cur.children.size(); i++) {
if (cur.children.get(i) != null) {
queue.add(cur.children.get(i));
}
}
}
int max = Integer.MIN_VALUE;
ArrayList<Node> ans = new ArrayList<Node>();
for (Entry<String, ArrayList<Node>> e : map.entrySet()) {
if (e.getKey().length() >= max) {
if (e.getKey().length() > max) {
ans.clear();
}
ans.addAll(e.getValue());
}
}
return ans;
}
private static String serialize(Node n) {
String signature = "";
LinkedList<Node> q = new LinkedList<Node>();
q.add(n);
if (n.children.size() == 0) {
signature = "0";
return signature;
}
Node curr = null;
while (!q.isEmpty()) {
curr = q.peek();
q.poll();
signature += String.valueOf(curr.children.size());
for (int i = 0; i < curr.children.size(); i++) {
q.offer(curr.children.get(i));
}
}
return signature;
}
JoJo, возможно, уже существует эффективный алгоритм для этой или подобной проблемы. Однако вот как я это сделаю.
import java.util.ArrayList;
import java.util.List;
public class Node
{
//signature = number of Node children + signature of each child = total number of all nodes below this Node (including children and children children...etc)
//
// for example, a tree would have it signature as
// 13
// 1 5 4
// 0 1 2 1 1
// 0 0 0 0 0
//
private int signature;
private int id;
private final List<Node> children;
private Node parent;
public Node(int id, Node parent) {
this.id = id;
this.children = new ArrayList<Node>();
this.parent = parent;
}
//updates signature, should be called every time there is a change to Node.
private void updateSignature() {
signature = children.size();
for(Node childNode : children) {
signature += childNode.signature;
}
//tell parent to update it signature
if(parent != null) {
parent.updateSignature();
}
}
//compares two trees to check if their structure is similar
private boolean hasSameStructureAs(Node otherNode) {
return otherNode != null && signature == otherNode.signature;
}
}