假设我有一个有向图,只有一个根,没有周期。我想在每个节点上添加一个类型(例如,作为带有一些自定义排序的整数),具有以下属性:
if Node1.type <= Node2.type then there exists a path from Node1 to Node2
请注意,拓扑排序实际上满足了反转属性:
if there exists a path from Node1 to Node2 then Node1.type <= Node2.type
因此不能在这里使用。
现在请注意,这里不能使用具有自然排序的整数,因为可以比较每2个整数,即整数的排序是线性的,而树不必是。
所以这是一个例子。假设图表有4个节点A, B, C, D
和4个箭头:
A->B, A->C, B->D, C->D
所以这是一颗钻石。现在我们可以把
A.type = 00
B.type = 01
C.type = 10
D.type = 11
在右侧,我们有二进制格式的整数。比较按位定义:
(X <= Y) if and only if (n-th bit of X <= n-th bit of Y for all n)
所以我想可以使用这样的排序,问题是如何从给定的图形构造值?我甚至不确定解决方案是否始终存在。任何提示?
更新:由于对我使用的术语存在一些误解,让我更明确一点:我对有向无环图感兴趣,这样只有一个节点没有前辈(又名根)并且在任意两个节点之间最多只有一个箭头。钻石就是一个例子。 不必须有一个叶子(即没有后继者的节点)。每个节点可能有多个前驱和多个后继。您可能会说这是具有最小元素的部分有序集(即唯一的全局最小元素)。
答案 0 :(得分:4)
您调用关系<=
,但它必然不完整(即:可能是给定对a
和b
,a <= b
也不{ {1}})。
以下是如何定义它的一个想法。
如果您的节点编号为0,1 ...,N-1,那么您可以像这样定义b <= a
:
type
并像这样定义type(i) = (1 << i) + sum(1 << (N + j), for j such that Path(i, j))
:
<=
也就是说,type1 <= type2 if (type1 >> N) & type2 != 0
在最低type(i)
位中编码i
的值,在最高N
位中编码所有可到达节点的集合。 N
关系在编码的可到达节点集中查找目标节点。
该定义适用于图中是否存在循环,实际上只是在您的节点集上编码任意关系。
通过使用<=
位对节点编码进行编码(每ceil(log2(N))
总共N + ceil(log2(N))
位),可以使定义更有效。
答案 1 :(得分:3)
对于任何DAG,您可以定义 x <= y
为“有从x到y的路径”。这种关系是部分顺序。我认为问题是如何有效地表达这种关系。
对于每个顶点X,将X定义为可从X到达的顶点集(包括X本身)。这两个陈述
是等价的。
将这些集编码为位集(N位二进制数),然后设置。
答案 2 :(得分:0)
问题说(并继续说)输入是树,但后来的编辑与钻石图的例子相矛盾。在这种非树案例中,我的算法将不适用。
现有的答案适用于一般有向图的一般关系,它将它们的表示大小膨胀为n个顶点的O(n)位。由于您有一棵树,因此可以使用更短的O(log n)位表示。
在远离根的树中,对于任何两个顶点u和v,分别从u和v可到达的叶子L(u)和L(v)的集合必须是不相交的,或者必须是另一个的子集。如果它们是不相交的,则无法从v到达(反之亦然);如果一个是另一个的适当子集,则具有较小集合的那个可以从另一个到达(并且在这种情况下,具有较小集合的那个将具有严格更大的深度)。如果L(u)= L(v),那么当且仅当深度(v)<1时,u才能从v到达。 depth(u),其中depth(u)是从根到u的路径上的边数。 (特别是,如果L(u)= L(v)且深度(u)=深度(v),则u = v。)
我们可以通过注意到从给定顶点v可到达的所有叶子通过树的顺序遍历占据叶子的连续片段来简明地编码这种关系。对于任何给定的顶点v,这组叶子因此可以由一对整数(first, last)
表示,first
标识第一个叶子(按顺序遍历顺序)和last
最后一个。从i到j的路径是否存在的测试非常简单 - 在伪C ++中:
bool doesPathExist(int i, int j) {
return x[i].first <= x[j].first && x[i].last >= x[j].last && depth[i] <= depth[j];
}
请注意,如果树中的每个非叶顶点至少有2个子节点,那么您不需要打扰深度,因为在这种情况下L(u)= L(v)表示u = v。 (我的帖子的原始版本做了这个假设;即使不是这样,我现在已经修复它了。)