Найти все поддеревья в дереве, соответствующие заданному поддереву в Java

Я пишу код на Java, который использует неупорядоченное корневое дерево, где каждый узел может иметь любое количество дочерних узлов. Имея дерево T и поддерево S, я хочу иметь возможность найти все поддеревья в T, соответствующие S (то есть все поддеревья в T, которые изоморфны S).

Поддерево T изоморфно S, если узлы S могут быть отображены на узлы T таким образом, что ребра S отображаются на ребра в T.

Был задан предыдущий вопрос о том, как определить, дерево содержит другое поддерево, однако я хочу найти ВСЕ поддеревья в T, которые соответствуют S. Кроме того, я хочу иметь возможность сопоставлять каждый узел в каждом совпадении в 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,y)]

считается дубликатом

[(a,x),(b,y),(c,z)]

поскольку они имеют одинаковый набор узлов из 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)]

Может ли кто-нибудь привести пример кода, как это сделать?

Редактировать (относительно комментария Криса Каннона):

Я думаю, вы хотите, чтобы кто-то закодировал ответ для вас? Как далеко вы зашли? Какой код вы написали? – Крис Кэннон 1 час назад

У меня есть следующий код, который при запуске создает список (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 спички, а не одна. Кроме того, код не строит сопоставление между узлами дерева и узлами поддерева, и я не могу понять, как это сделать.

Кто-нибудь может дать совет, как это сделать?


person Community    schedule 20.01.2010    source источник
comment
Я думаю, вы хотите, чтобы кто-то закодировал ответ для вас? Как далеко вы зашли? Какой код вы написали?   -  person Chris Kannon    schedule 20.01.2010
comment
+1 за отличное объяснение ... ну, на самом деле, на сегодня все голоса закончились, но важно намерение   -  person Anurag    schedule 20.01.2010
comment
@Chris Kannon : я обновил вопрос в ответ на ваш комментарий и включил код, написанный до сих пор.   -  person tree-hacker    schedule 20.01.2010


Ответы (2)


Я думаю, что ваш рекурсивный метод должен возвращать список частичных совпадений, а не просто логическое значение. Это будет иметь большое значение для решения обеих ваших проблем (необходимость вернуть список совпадений, а также найти несколько совпадений).

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 и не добавляет это в список. Звонящий должен это сделать. Эту функцию нужно запускать на каждом узле дерева.

В настоящее время он, вероятно, использует слишком много памяти, но это можно оптимизировать.

person Community    schedule 23.01.2010

Это может оказаться полезным.

person Community    schedule 20.01.2010
comment
Спасибо. Да, я уже прочитал эти заметки об изоморфизме деревьев и дополнительные слайды об изоморфизме поддеревьев ( lsi.upc.es/~valiente/riga-tre.pdf ), однако я не мог понять, как перевести приведенные алгоритмы в код, особенно в отношении того, как построить сопоставление между узлами в поддереве. и узлы в совпадениях в дереве. есть идеи как это сделать? - person tree-hacker; 20.01.2010