Обнаружить различия между древовидными структурами - PullRequest
70 голосов
/ 05 мая 2011

Это больше вопрос CS, но интересный:

Допустим, у нас есть 2 древовидные структуры с более или менее реорганизованными одинаковыми узлами.Как бы вы нашли

  1. любой
  2. в некотором смысле минимальный

последовательность операций

  • MOVE(A, B) - перемещает узел A под узлом B (со всем поддеревом)
  • INSERT(N, B) - вставляет новый узел N под узлом B
  • DELETE (A) - удаляет узел A (со всем поддеревом)

, который преобразует одно дерево в другое.

Очевидно, что могут быть случаи, когда такое преобразование невозможно, тривиально - быть корнем A с ребенком B и корнем B с ребенком A и т. д.).В таких случаях алгоритм просто выдаст результат « невозможен ».

Еще более впечатляющий вариант - обобщение для сетей, т.е. когда мы предполагаем, что узел может встречаться несколько раз вдерево (фактически с несколькими «родителями»), в то время как циклы запрещены.

Отказ от ответственности: Это не домашнее задание, на самом деле оно исходит из реальной бизнес-проблемы, и я нашел ее довольно интереснойинтересно, может кто-нибудь знает решение.

Ответы [ 4 ]

21 голосов
/ 05 мая 2011

Существует не только статья в Википедии об изоморфизме графа (как указывает Space_C0wb0y), но и отдельная статья по проблеме изоморфизма графа .Он имеет секцию Solved special cases, для которой известны решения за полиномиальное время.Деревья являются одним из них, и он цитирует следующие две ссылки:

14 голосов
/ 11 мая 2011

Вам неясно, сравнивали ли вы деревья абстрактного синтаксиса для исходного кода, документы XML, интерпретируемые как деревья, или какое-то другое дерево типа.

В ряде статей обсуждается сравнение синтаксических деревьев и вычисление минимальных расстояний различными способами. Идеи должны быть актуальными.

Хорошая статья - Change Distilling , которая пытается сравнить исходный код для двух абстрактных синтаксических деревьев и сообщить минимальную разницу. В статье рассказывается о конкретном методе, а также кратко упоминается (и даются ссылки) на различные похожие методы.

Немногие из этих алгоритмов фактически реализованы в доступных инструментах для сравнения исходного текста. Наш Smart Differencer является одним из них.

11 голосов
/ 02 мая 2015

Хотя этот вопрос старый, я добавлю еще несколько ссылок и алгоритмов ниже:

  1. X-Diff: эффективный алгоритм обнаружения изменений для документов XML, Юань Ван, Дэвид Дж. ДеВитт, Джин-Йи Кай
  2. KF-Diff +: высокоэффективный алгоритм обнаружения изменений для документов XML
  3. diffX: алгоритм для обнаружения изменений в многоверсионной версии XML документы
  4. Обнаружение изменений в деревьях XML: обзор, Луук Петерс
  5. Сходство в древовидных структурах данных

Кроме того, в GitHub есть библиотеки и интегрированные среды (в javascript), которые реализуют различие древовидных структур, например, в приложениях, работающих с данными JSON или деревьями XML (например, для MVC / MVVM на стороне клиента):

  1. React.js
  2. JSON-Patch
  3. jsondiffpatch
  4. objectDiff
8 голосов
/ 15 июня 2013

Если люди найдут этот вопрос и ему понадобится что-то для Node.js или браузера, я предоставлю ссылку и пример кода для написанной мной реализации, которую вы можете найти на github здесь: (https://github.com/hoonto/jqgram.git) на основе существующего кода PyGram Python (https://github.com/Sycondaman/PyGram).

Это алгоритм редактирования дерева расстояний аппроксимация , но он намного, намного быстрее, чем попытка найти истинное расстояние редактирования.Аппроксимация выполняется за время O (n log n) и пространство O (n), тогда как истинное расстояние редактирования часто составляет O (n ^ 3) или O (n ^ 2) с использованием известных алгоритмов для истинного расстояния редактирования.алгоритм PQ-Gram имеет вид: (http://www.vldb2005.org/program/paper/wed/p301-augsten.pdf)

Итак, используя jqgram:

Пример:

var jq = require("jqgram").jqgram;
var root1 = {
    "thelabel": "a",
    "thekids": [
        { "thelabel": "b",
        "thekids": [
            { "thelabel": "c" },
            { "thelabel": "d" }
        ]},
        { "thelabel": "e" },
        { "thelabel": "f" }
    ]
}

var root2 = {
    "name": "a",
    "kiddos": [
        { "name": "b",
        "kiddos": [
            { "name": "c" },
            { "name": "d" },
            { "name": "y" }
        ]},
        { "name": "e" },
        { "name": "x" }
    ]
}

jq.distance({
    root: root1,
    lfn: function(node){ return node.thelabel; },
    cfn: function(node){ return node.thekids; }
},{
    root: root2,
    lfn: function(node){ return node.name; },
    cfn: function(node){ return node.kiddos; }
},{ p:2, q:3 },
function(result) {
    console.log(result.distance);
});

И это дает число от 0 до 1.чем ближе к нулю, тем более тесно связанные два дерева смотрят на jqgram. Одним из подходов может быть использование jqgram для сужения нескольких тесно связанных деревьев из числа множества деревьев с учетом его скорости, а затем использование истинного расстояния редактирования наосталось несколько деревьев, которые вам нужно более внимательно изучить, и для этого вы можете найти реализации Python для ссылки или порта алгоритма Чжан и Шаша, например.

Обратите внимание, что параметры lfn и cfn определяют, как каждыйtree должен определять имена меток узла и дочерний массив для каждого корня дерева независимо, так что вы можете делать такие интересные вещи, как, например, сравнение объекта с DOM браузера.Все, что вам нужно сделать, это предоставить эти функции вместе с каждым корнем, а jqgram сделает все остальное, вызвав предоставленные вами функции lfn и cfn для построения деревьев.Так что в этом смысле (на мой взгляд, в любом случае) его гораздо проще использовать, чем PyGram.Кроме того, это Javascript, так что используйте его на стороне клиента или на стороне сервера!

ТАКЖЕ, чтобы ответить относительно обнаружения циклов, проверьте метод клонирования внутри jqgram, там есть обнаружение циклов, но заслуга в этом идетавтору узла-клона, из которого эта часть была слегка изменена и включена.

...