计算每个节点的深度"最大包装" DAG

时间:2016-04-01 03:23:29

标签: algorithm graph-algorithm graphing directed-acyclic-graphs

(注意:我考虑在https://cstheory.stackexchange.com/上问这个问题,但我认为我的问题不够理论 - 它是关于算法的。如果有一个更好的Stack Exchange社区发帖,我很高兴听!)

我使用术语"起始节点"表示没有链接的节点,"终端节点"表示没有链接的节点。因此下图包含起始节点A和B以及终端节点F和G:

image of a Directed Acyclic Graph

我想用以下规则绘制它:

  • 至少一个起始节点的深度为0。
  • 链接总是从上到下指向
  • 节点尽可能垂直地打包

使用这些规则,上图显示了每个节点的深度。有人可以建议一种算法来计算在不到O(n ^ 2)的时间内运行的每个节点的深度吗?

更新

我调整了图表以显示DAG可能包含不同深度的起始节点和终端节点。 (这是我在原来的错误答案中没有考虑过的情况。)我还从" x坐标"中切换了术语。到"深度"为了强调这是关于"图表"而不是"图形"。

2 个答案:

答案 0 :(得分:0)

节点的x坐标对应于从任何节点开始的最长路径,而不会在此问题上出现边缘。对于DAG,可以在O(N)

中计算
given DAG G:
  calculate incomming_degree[v] for every v in G
  initialize queue q={v with incomming_degree[v]==0}, x[v]=0 for every v in q
  while(q not empty):
     v=q.pop()  #retreive and delete first element
     for(w in neighbors of v):
          incomming_degree[w]--
          if(incomming_degree[w]==0): #no further way to w exists, evaluate
               q.offer(w)
               x[w]=x[v]+1

x存储所需信息。

答案 1 :(得分:0)

这是一个基本上是两遍深度优先树步行的解决方案。第一遍(traverseA)跟踪起始节点(在O.P。的示例中为A和B)的DAG,直到遇到终端节点(示例中为F和G)。它们用图中描绘的最大深度标记它们。

第二遍(traverseB)从终端节点开始并追溯到起始节点,沿着节点的当前值或前一节点的值减一,标记每个节点,以较小者为准如果尚未访问该节点,则该值更小:

function labelDAG() {
    nodes.forEach(function(node) { node.depth = -1; });  // initialize
    // find and mark terminal nodes
    startingNodes().forEach(function(node) { traverseA(node, 0); });
    // walk backwards from the terminal nodes
    terminalNodes().forEach(function(node) { traverseB(node); });
    dumpGraph();
};

function traverseA(node, depth) {
    var targets = targetsOf(node);
    if (targets.length === 0) {
        // we're at a leaf (terminal) node -- set depth
        node.depth = Math.max(node.depth, depth);
    } else {
        // traverse each subtree with depth = depth+1
        targets.forEach(function(target) {
            traverseA(target, depth+1);
        });
    };
};

// walk backwards from a terminal node, setting each source node's depth value
// along the way.
function traverseB(node) {
    sourcesOf(node).forEach(function(source) {
        if ((source.depth === -1) || (source.depth > node.x - 1)) {
            // source has not yet been visited, or we found a longer path
            // between terminal node and source node.
            source.depth = node.depth - 1;
        }
        traverseB(source);
    });
};