Merkle Tree 算法

最近新写了一个算法,成功的把算法复杂度从O(N^3) 降到了 O(N), 后来发现这原来是大名鼎鼎的Merkle tree 算法。(I reinvent the wheel. :))

这个算法的应用场景很多,从区块链的核心算法到 NoSql的内存树,都是这个算法或者其变种。

正好项目要结束了,把这个算法好好理一理,也算是做个记录。


树的数据结构在计算机中用处很大,比如树可以用来表示文件的结构,比如表示组织关系。如果现在有两个树(树A 和 树B),需要对这两个树做合并,生成一个新的树。做树的合并的第一步是寻找子树,也就是说给定树A的一个子树,判断这棵子树是否存在与树B中间。

从树A的根节点开始,然后从树B的根节点找起,遍历树看看是否两者相同,如果有不一样的,那么看这个根节点的子节点,再次比较。假设树B的节点数目为N,那么复杂度为N^2。假设树A的节点数目为M,那么遍历树A和树B的复杂度为M * N^2。特别的,假设M 约等于N,那么总的复杂度为O(N^3)。

具体的分析,可以看 A Survey on Tree Edit Distance and Related Problems。里面总结的比较详细。


因为这是一个通用的问题的通用解法,所以复杂度比较高 (O(N^3))。可以通过给问题设置合理的假设,降低复杂度。


首先定义树的相等。

假设树A和树B,A的根节点和 B的根节点相等 (这节点相等可以自行定义),数量相等,并且子节点也各自相同,那么这个根节点就相同。


一个假设:

如果要比较两个树,那么节点应该在两个树的同一层。如果在同一层没有找到,则节点不存在。

同时,树的合并和SQL查询一样,也有left merge, right merge, inner merge and full merge。

看个例子,比如下面树A和树B做full merge。

树A

1

2,3

树B

1

3,4

2

合并后树C:

1

2,3,4

2


可以再想一想怎样做left merge 和 inner merge。

对于一个树来说,怎么快速判断树A和树B相等呢?(想想前面的相等定义)。一个通用思路是用hash。如果所有的子节点都有自己的hash。那么把所有的子节点的hash 加上根节点的hash做hash,得到一个hash值。如果两个树的hash值相等,那么树肯定相等。反之亦然。

到这里,我们就得到了Merkle Tree。

简单的说,Merkle Tree是一个多叉hash树,可以快速的做比较,方便树与树之间做merge。


应用场景:

最著名的场景应该是blockchain的使用了。每个节点都有一个自己的账本,这个账本是一个多叉树(因为有不同的分支提交,所以有主线支路之分)。每个节点的账本会不定时的和其他节点的账本做合并。在这里,合并的效率很关键,所以使用了Merkle Tree作为基本的数据结构。如果只要合并最新的值,Merkle Tree可以提升到O(1)的效率,最慢也就O(N)。这为blockchain下面的bitcoin的大量用户使用打下了基础。

编辑于 2018-01-24