在加权二叉树中查找最重的长度约束路径

时间:2011-02-21 02:20:58

标签: algorithm theory binary-tree

更新

我计算出一种我认为在O(n * k)运行时运行的算法。下面是伪代码:

routine heaviestKPath( T, k )

    // create 2D matrix with n rows and k columns with each element = -∞
    // we make it size k+1 because the 0th column must be all 0s for a later 
    // function to work properly and simplicity in our algorithm
    matrix = new array[ T.getVertexCount() ][ k + 1 ] (-∞);

    // set all elements in the first column of this matrix = 0
    matrix[ n ][ 0 ] = 0;

    // fill our matrix by traversing the tree
    traverseToFillMatrix( T.root, k );

    // consider a path that would arc over a node
    globalMaxWeight = -∞;
    findArcs( T.root, k );

    return globalMaxWeight
end routine


// node = the current node; k = the path length; node.lc = node’s left child; 
// node.rc = node’s right child; node.idx = node’s index (row) in the matrix; 
// node.lc.wt/node.rc.wt = weight of the edge to left/right child;
routine traverseToFillMatrix( node, k )

   if (node == null) return;

   traverseToFillMatrix(node.lc, k ); // recurse left
   traverseToFillMatrix(node.rc, k ); // recurse right

   // in the case that a left/right child doesn’t exist, or both,
   // let’s assume the code is smart enough to handle these cases
   matrix[ node.idx ][ 1 ] = max( node.lc.wt, node.rc.wt );


   for i = 2 to k {
       // max returns the heavier of the 2 paths
       matrix[node.idx][i] = max( matrix[node.lc.idx][i-1] + node.lc.wt, 
                                  matrix[node.rc.idx][i-1] + node.rc.wt);
   }

end routine



// node = the current node, k = the path length
routine findArcs( node, k ) 

    if (node == null) return;

    nodeMax = matrix[node.idx][k];
    longPath = path[node.idx][k];

    i = 1;
    j = k-1;
    while ( i+j == k AND i < k ) {

        left = node.lc.wt + matrix[node.lc.idx][i-1];
        right = node.rc.wt + matrix[node.rc.idx][j-1];

        if ( left + right > nodeMax ) {
            nodeMax = left + right;
        }
        i++; j--;
    }

    // if this node’s max weight is larger than the global max weight, update
    if ( globalMaxWeight < nodeMax ) {
        globalMaxWeight = nodeMax;
    }

    findArcs( node.lc, k ); // recurse left
    findArcs( node.rc, k ); // recurse right

end routine

让我知道你的想法。欢迎提供反馈。


认为已经提出了两种天真的算法,它们在加权二进制树中找到最重的长度约束路径。首先,该算法的描述如下:给定一个带有加权边和一些值k的n-顶点二叉树,找到长度为k的最重路径。

对于这两种算法,我需要对所有顶点的引用,所以我只需要对树进行简单的遍历,以便对所有顶点进行引用,每个顶点都有对其左,右和父的引用树中的节点。

算法1 对于这个算法,我基本上计划从树中的每个节点运行DFS,同时考虑固定的路径长度。另外,由于我正在寻找的路径有可能从左子树到根到右子树,我将不得不考虑每个节点的3个选择。但这会导致O(n * 3 ^ k)算法,我不喜欢这样。

算法2 我基本上考虑使用Dijkstra's Algorithm的修改版本来考虑固定的路径长度。由于我正在寻找最重的并且Dijkstra的算法找到最轻的,我打算在开始遍历之前否定所有边缘权重。实际上......这没有意义,因为我必须在每个节点上运行Dijkstra,并且这似乎不比上述算法更有效。

所以我想我的主要问题有几个。首先,我上面描述的算法能解决手头的问题吗?我并不完全确定Dijkstra的版本会起作用,因为Dijkstra的版本是为了获得正边缘值。

现在,我确信存在更多聪明/有效的算法...什么是更好的算法?我读过“使用脊柱分解来有效地解决树长度受限的最重路径问题”,但这真的很复杂,我根本不理解它。是否有其他算法可以解决这个问题,可能不像脊柱分解那样有效但更容易理解?

3 个答案:

答案 0 :(得分:3)

您可以在 k 边缘之后停止的每个节点向下使用DFS来搜索路径,但请注意,这将在每个节点上执行2 ^ k工作,总共为O(n * 2 ^ k)工作,因为从起始节点开始,每个级别的路径数量都会翻倍。

正如DasBoot在评论中所说的那样,在这里使用Dijkstra算法没有任何优势,因为它的聪明程度相当于选择最短(或最长)的方式来获得多个路线可能的两个点之间。有了树,总有一条路。

我有一个动态编程算法需要O( nk )时间。以下是一些提示:

  • 如果您选择一些叶子顶点作为根 r 并将所有其他顶点向下引导,远离根,请注意此有向树中的每个路径都有最高节点 - 即最接近 r 的唯一节点。
  • 您可以通过遍历每个节点 v 并计算最高的最长 - k 路径来计算最重的长度 k 路径节点是 v ,最后在所有节点上取最大值。
  • 最高节点为 v 的长度 k 路径必须具有朝向一个子节点和长度的长度 i 路径 - ( ki )路径向另一条路径下降。

这应该足以让你思考正确的方向;如果您需要进一步的帮助,请告诉我。

答案 1 :(得分:2)

这是我的解决方案。欢迎提供反馈。

让我们将二叉树视为有向图,边缘从父级到子级。让我们为每个顶点v定义两个概念:

a)弧:它是一个有向路径,也就是说,它从顶点v开始,路径中的所有顶点都是起始顶点v的子节点。

b)一个子路径:这是一个包含v的定向或非定向路径,也就是说,它可以从任何地方开始,到任何地方结束,从v的孩子到v,然后,对其他孩子说。弧集是子路径集的子集。

我们还定义了一个函数HeaviestArc(v,j),它为顶点j给出了最长的弧,在左侧或右侧,长度为j,从v开始。我们还定义了LeftHeaviest(v,j) )和RightHeaviest(v,j)分别是长度为j的最重的左右弧。

鉴于此,我们可以根据子节点v为每个顶点v定义以下重复:

LeftHeaviest(v,j) = weight(LeftEdge(v)) + HeaviestArc(LeftChild(v)),j-1);
RightHeaviest(v,j) = weight(RightEdge(v)) + HeaviestArc(RightChild(v)),j-1);
HeaviestArc(v,j) = max(LeftHeaviest(v,j),RightHeaviest(v,j));

这里j从1到k,HeaviestArc(v,0)= LeftHeaviest(v,0),RightHeaviest(v,0)= 0表示全部。对于叶节点,HeaviestArc(v,0)= 0,而HeaviestArc(v,j)= - inf用于所有其他j(我需要更彻底地考虑角点情况)。

然后HeaviestChildPath(v),包含v的最重的子路径可以计算为:

HeaviestChildPath(v) = max{ for j = 0 to k LeftHeaviest(j) + RightHeaviest(k-j)}

最重的路径应该是所有儿童路径中最重的路径。

算法的估计运行时间应为O(kn)。

答案 2 :(得分:0)

def traverse(node, running_weight, level):
  if level == 0: 
    if max_weight < running_weight:
        max_weight = running_weight
    return
  traverse(node->left,running_weight+node.weight,level-1)
  traverse(node->right,running_weight+node.weight,level-1)
  traverse(node->parent,running_weight+node.weight,level-1)


max_weight = 0
for node in tree:
  traverse(node,0,N)