在加权二叉树中查找最重的长度约束路径
更新
我制定了一个我认为运行时间为 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 算法 的修改版本,以便考虑修复路径长度。由于我正在寻找最重的并且 Dijkstra 算法找到最轻的,因此我计划在开始遍历之前否定所有边权重。实际上...这没有意义,因为我必须在每个节点上运行 Dijkstra's,而且这似乎并不比上述算法有效得多。
所以我想我的主要问题有几个。首先,我上面描述的算法是否解决了当前的问题?我不完全确定 Dijkstra 的版本是否有效,因为 Dijkstra 的版本适用于正边缘值。
现在,我确信存在更聪明/更有效的算法......什么是更好的算法?我读过“使用脊柱分解有效解决树的长度约束最重路径问题”,但这确实很复杂,我根本不理解。是否有其他算法可以解决这个问题,可能不如脊柱分解那么有效,但更容易理解?
UPDATE
I worked out an algorithm that I think runs in O(n*k) running time. Below is the pseudo-code:
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
Let me know what you think. Feedback is welcome.
I think have come up with two naive algorithms that find the heaviest length-constrained path in a weighted Binary Tree. Firstly, the description of the algorithm is as follows: given an n-vertex Binary Tree with weighted edges and some value k, find the heaviest path of length k.
For both algorithms, I'll need a reference to all vertices so I'll just do a simple traversal of the Tree to have a reference to all vertices, with each vertex having a reference to its left, right, and parent nodes in the tree.
Algorithm 1
For this algorithm, I'm basically planning on running DFS from each node in the Tree, with consideration to the fixed path length. In addition, since the path I'm looking for has the potential of going from left subtree to root to right subtree, I will have to consider 3 choices at each node. But this will result in a O(n*3^k) algorithm and I don't like that.
Algorithm 2
I'm essentially thinking about using a modified version of Dijkstra's Algorithm in order to consider a fixed path length. Since I'm looking for heaviest and Dijkstra's Algorithm finds the lightest, I'm planning on negating all edge weights before starting the traversal. Actually... this doesn't make sense since I'd have to run Dijkstra's on each node and that doesn't seem very efficient much better than the above algorithm.
So I guess my main questions are several. Firstly, do the algorithms I've described above solve the problem at hand? I'm not totally certain the Dijkstra's version will work as Dijkstra's is meant for positive edge values.
Now, I am sure there exist more clever/efficient algorithms for this... what is a better algorithm? I've read about "Using spine decompositions to efficiently solve the length-constrained heaviest path problem for trees" but that is really complicated and I don't understand it at all. Are there other algorithms that tackle this problem, maybe not as efficiently as spine decomposition but easier to understand?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您可以使用从每个节点向下的 DFS,在 k 条边之后停止来搜索路径,但请注意,这将在每个节点上执行 2^k 工作,总共 O(n*2^k )有效,因为从起始节点向下的每个级别,路径数量都会加倍。
正如 DasBoot 在评论中所说,在这里使用 Dijkstra 算法没有任何优势,因为它的聪明之处在于,当有多个路线可能时,选择最短(或最长)的方式到达两点之间。对于一棵树来说,总是只有一种方法。
我有一个动态规划算法,需要 O(nk) 时间。这里有一些提示:
这应该足以让你朝着正确的方向思考;如果您需要进一步的帮助,请告诉我。
You could use a DFS downwards from each node that stops after k edges to search for paths, but notice that this will do 2^k work at each node for a total of O(n*2^k) work, since the number of paths doubles at each level you go down from the starting node.
As DasBoot says in a comment, there is no advantage to using Dijkstra's algorithm here since it's cleverness amounts to choosing the shortest (or longest) way to get between 2 points when multiple routes are possible. With a tree there is always exactly 1 way.
I have a dynamic programming algorithm in mind that will require O(nk) time. Here are some hints:
That should be enough to get you thinking in the right direction; let me know if you need further help.
这是我的解决方案。欢迎反馈。
让我们将二叉树视为有向图,边从父级到子级。让我们为每个顶点 v 定义两个概念:
a) 弧:这是一条有向路径,即从顶点 v 开始,路径中的所有顶点都是起始顶点 v 的子节点。
b) 子路径:这是一条包含 v 的有向或无向路径,也就是说,它可以从任何地方开始,在任何地方结束,从 v 的孩子到 v,然后到它的另一个孩子。弧集是子路径集的子集。
我们还定义了一个函数 HeaviestArc(v,j),它给出了顶点 j 的最重弧,在左侧或右侧,长度为 j,从 v 开始。我们还定义了 LeftHeaviest(v,j),并且RightHeaviest(v,j) 分别为长度为 j 的最重左弧和右弧。
鉴于此,我们可以根据其子节点为每个顶点 v 定义以下递归:
这里 j 从 1 到 k,并且 HeaviestArc(v,0)=LeftHeaviest(v,0),RightHeaviest(v,0)=全部为 0。对于叶节点,HeaviestArc(v,0) = 0,对于所有其他 j,HeaviestArc(v,j)=-inf(我需要更彻底地考虑极端情况)。
然后,包含 v 的最重子路径 HeaviestChildPath(v) 可以计算为:
最重路径应该是所有子路径中最重的。
算法的估计运行时间应该是 O(kn) 阶。
Here's my solution. Feedback is welcome.
Lets treat the binary tree as a directed graph, with edges going from parent to children. Lets define two concepts for each vertex v:
a) an arc: which is a directed path, that is, it starts from vertex v, and all vertices in the path are children of the starting vertex v.
b) a child-path: which is a directed or non-directed path containing v, that is, it could start anywhere, end anywhere, and go from child of v to v, and then, say to its other child. The set of arcs is a subset of the set of child-paths.
We also define a function HeaviestArc(v,j), which gives, for a vertex j, the heaviest arc, on the left or right side, of length j, starting at v. We also define LeftHeaviest(v,j), and RightHeaviest(v,j) as the heaviest left and right arcs of length j respectively.
Given this, we can define the following recurrences for each vertex v, based on its children:
Here j here goes from 1 to k, and HeaviestArc(v,0)=LeftHeaviest(v,0),RightHeaviest(v,0)=0 for all. For leaf nodes, HeaviestArc(v,0) = 0, and HeaviestArc(v,j)=-inf for all other j (I need to think about corner cases more thoroughly).
And then HeaviestChildPath(v), the heaviest child-path containing v, can be calculated as:
The heaviest path should be the heaviest of all child paths.
The estimated runtime of the algorithm should be order O(kn).