检测树结构之间的差异

时间:2011-05-05 08:41:24

标签: algorithm tree comparison diff computer-science

这更像是一个CS问题,但却是一个有趣的问题:

假设我们有2个树结构,其中重组的节点或多或少相同。你怎么找到

  1. 任何
  2. 在某种意义上 minimal
  3. 操作顺序

    • MOVE(A, B) - 在节点B下移动节点A(使用整个子树)
    • INSERT(N, B) - 在节点B下插入节点N
    • DELETE (A) - 删除节点A(包含整个子树)

    将一棵树转换为另一棵树。

    显然可能存在不可能进行这种转换的情况,例如根据孩子B到根B与孩子A等的根A。在这种情况下,算法只会提供“不可能”的结果。

    更为壮观的版本是网络的概括,即当我们假设一个节点可以在树中多次出现(实际上有多个“父母”)时,禁止循环。

    免责声明:这是一项功课,实际上它来自一个真正的商业问题,我觉得很有意思,如果有人可能知道解决方案。

5 个答案:

答案 0 :(得分:21)

关于图同构的维基百科文章(如Space_C0wb0y指出的那样),还有一篇关于graph isomorphism problem的专题文章。它有一个Solved special cases部分,其中已知多项式时间解。树木就是其中之一,它引用了以下两个参考文献:

答案 1 :(得分:14)

您不清楚是否要比较源代码的抽象语法树,解释为树的XML文档或其他类型的树。

有很多论文讨论通过各种方式比较语法树和计算最小距离。这些想法应该是相关的。

一篇好文章是Change Distilling,它试图比较两个抽象语法树的源代码并报告最小差异。本文讨论了一种特定的方法,并且还简要地提及(并提供了参考)各种类似的技术。

在用于比较源文本的可用工具中实际上实现了这些算法中的很少一部分。我们的Smart Differencer就是其中之一。

答案 2 :(得分:11)

虽然这个问题很老,但我会在下面添加更多参考和算法:

  1. X-Diff: An Effective Change Detection Algorithm for XML Documents , Yuan Wang, David J. DeWitt, Jin-Yi Cai
  2. KF-Diff+: Highly Efficient Change Detection Algorithm for XML Documents
  3. diffX: An Algorithm to Detect Changes in Multi-Version XML Documents
  4. Change Detection in XML Trees: a Survey, Luuk Peters
  5. Similarity in Tree Data Structures
  6. 此外,GitHub上的库和框架(在javascript中)实现了树状结构的差异化,例如处理JSON数据或XML树的应用程序(例如客户端MVC / MVVM):

    1. React.js
    2. JSON-Patch
    3. jsondiffpatch
    4. objectDiff

答案 3 :(得分:8)

如果人们发现这个问题并且需要为Node.js或浏览器实现一些东西,我将为我编写的一个实现提供一个链接和代码示例,您可以在github上找到它:({{3} })基于现有的PyGram Python代码(https://github.com/hoonto/jqgram.git)。

这是树编辑距离近似算法,但它比尝试找到真正的编辑距离要快得多。近似在O(n log n)时间和O(n)空间中执行,而真正的编辑距离通常是使用用于真实编辑距离的已知算法的O(n ^ 3)或O(n ^ 2)。请参阅PQ-Gram算法所来自的学术论文:(https://github.com/Sycondaman/PyGram

所以使用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实现参考或端口的张&例如Shasha算法。

请注意,lfn和cfn参数指定每个树应如何独立地确定每个树根的节点标签名称和子数组,以便您可以执行诸如将对象与浏览器DOM进行比较等时髦的事情。您需要做的就是提供这些函数以及每个根,jqgram将完成其余的工作,调用您的lfn和cfn提供的函数来构建树。所以从这个意义上来说,(在我看来)它比PyGram更容易使用。另外,它的Javascript,所以使用它客户端或服务器端!

另外,为了回答循环检测,检查jqgram里面的克隆方法,那里有循环检测,但是归功于节点克隆的作者,该片段稍微修改并包含在其中。

答案 4 :(得分:0)

这称为树到树校正问题树到树编辑问题。由于某些原因,处理此问题的大多数文献都明确地涉及到比较XML树,因此搜索“ XML diffing algorithm”会产生很多结果。除了Nikos的链接列表之外,我还发现了以下这些内容:

我也强烈建议您阅读Change Detection in XML Trees: a Survey,但这是从2005年开始的,因此它所提到的任何工具都不再存在。 将XML文档作为具有参考感知能力的标记有序树进行比较对到目前为止我发现的某些算法(从2.1.2节开始)进行了最直观的描述。

不幸的是,似乎没有太多开放源代码可以做到这一点,而且它不是很古老。只是很多过于复杂的论文。 :-/