什么是维稳算法?

维稳算法(Order Maintenance)是儒家思想君君臣臣的集大成者 - 这个算法使我们可以拿取任意两个事物,然后进行一个全序比较。

换言之,Order Maintenance可以看成一个链表,但是链表的所有元素直接可以快速比较前后。

也就是说,OM带有以下三个api:

  • Head(): OM,取得链表头
  • Create(before:OM):OM,在before后插入一个节点
  • <=(lhs: OM, rhs: OM): bool,判断lhs是否在rhs的前面

OM的实现不是这篇文章的重点,就放在Order Maintenance - Google Slide这里了。

最近工作中发现了动态LCA(给予一棵树,处理树上的修改穿插着LCA查询)可以用OM解决。

另外碎碎念:Order Maintenance在Incremental Computing,Persistent Data Structure,跟String Algorithm上面都有应用,已经属于我走到那跟到那的变态跟踪狂了,下头算法!必须告它性骚扰!

一般来说,这个问题可以用link cut tree解决,两者复杂度一样,但是思路跟工具都不一样。特别是用到了维稳算法!维稳算法解动态LCA也有各种各样的extension,比如魔改一下可以用来增量维护binding in lambda calculus。

具体操作如下:

我们可以给树的每个节点一个OM区间,并且用preorder traversal进出时间维护区间开闭。

具体操作是,给一颗新的树,我们可以维护一个OM timer。此timer在traversal递归进/出的时候会各进行一次自增,并且用自增前的两个值来初始化OM区间。这样,所有OM值都是独一无二的,并且树的区间包含子树的区间。同时,无直系亲属关系的两节点区间不相交。

新加入的叶子节点的区间可以用兄或(如果兄不存在)父的区间右端点连续插入两个值初始化。

我们维护一个装着区间的平衡二叉树。区间的序是左端点的序,而BST每一节点储存"该BST子树的最大右端点"。

这时候,找寻多个节点的LCA,成为了‘找寻最大的(左端点最右),包含所有节点区间的区间’。

这可以分两步完成:

0:去除所有不够左的左端点。由于是暂时的,我们不需维护平衡性,只需维护子树最大右端点,所以是O(log(n))

1:在所有右端点足够右的子树里,寻找最右的左端点。

具体操作是:

  • 当右子树子树最大右端点足够大的时候,递归进去。
  • 否则,如果当下节点右端点足够大,返回。
  • 否则,左子树子树最大右端点足够大,递归进去。

这是log(n),跟link cut tree大O一样(但我认为更好懂也更优雅!)

另外顺带一提:Edward Kmett对这个问题的函数式特化版做了一个改进,达到了O(log(height)),见

On-line Lowest Common Ancestor