如何在多树中查找最大的公共子树

时间:2014-09-24 00:26:19

标签: java algorithm serialization data-structures tree

最近,我遇到了一个算法问题:树被定义为

public class Node 
{
    int id;      
    private final List<Node> children;
    Node(int id) {
        this.id = id;
        this.children = new ArrayList<>();
    }      
} 
  

如果两个子树的结构完全相同,则它们是共同的。最大的公共子树最大化每个子树中的节点数。那么如何找到maxmum公共子树(每个节点的id无关紧要,只是子树的结构是相同的)。如果存在具有相同最大大小的单独子树组,那么我们应该从所有子树返回根节点。

我的想法是使用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;
    }

1 个答案:

答案 0 :(得分:0)

JoJo,对于这个或类似的问题,可能已经存在一种有效的算法。但是,我会这样做。

import java.util.ArrayList;
import java.util.List;


public class Node
{
    //signature = number of Node's children + signature of each child     =  total number of all nodes below this Node (including children and children's children...etc)
    //
    // for example, a tree would have it's 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's 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;
    }
}