在树中查找简单路径的最小权重

时间:2015-02-11 17:09:07

标签: algorithm data-structures graph tree shortest-path

我遇到了一个问题,我们想设计一种有效的算法来找到一个重量最轻的简单路径。 (最小权重的简单路径)。

我认为这不是多项式解决方案,但有些朋友说可能是O(n)或O(n ^ 2)或O(n lg n)将是......!

程序员或专家,会帮助我吗?任何伪君子?

编辑:

此问题的输入是树T,边缘上有整数权重。权重可以是负数,零或正数。路径的长度是路径中边缘的权重之和。如果没有重复顶点,则路径很简单。

输出:在给定树中找到最小权重的简单路径。

3 个答案:

答案 0 :(得分:0)

在树中,每对节点(there's a proof here)之间只有一条简单的路径。

因此,即使您尝试在每对节点之间找到路径,也可以使用O(n ^ 3)算法。

一种更好的方法是为每个节点找到一次访问中每个其他节点的成本。这会将算法降低到O(n ^ 2)。

答案 1 :(得分:0)

有一个简单的O(n)算法,它基于这样一个事实:在树中总有一些叶子,如果我们删除叶子,剩下的图形仍然是树。所以我们可以逐个删除叶子,并通过这种方式处理所有树(所有边缘)。

您需要为每个节点保留最轻的路径。

因此,假设您正在处理一些叶子,并且节点叶子连接到节点n,我们希望确保您密切关注通过连接此n和叶子的边缘的任何路径。让我们来看看图片

enter image description here

此处的方向仅表示从早期删除的节点开始的方向" parent",稍后将删除的节点,它可能取决于您正在处理的顺序n。

如果我们有通过叶子的最短路径,那么它可以连接任何"孩子"与n的其他孩子的叶子或它可以连接任何"孩子"叶子到树的其余部分。

在这两种情况下,它足以保持从已处理的任何节点到当前节点的最短路径。当我们处理叶子时,我们采取最轻的路径为叶子建立并增加连接的重量并将其与已经为n保存的最短路径进行比较。通过这种方式,我们不可避免地会比较n的两条最短路径,并且能够将它们联合起来,并且当所有" children"将处理n。

所以这里是伪代码。

for each (node in graph)
{
   node.shortest_path = 0; //zero length path from this node to this node
}
leaves = new list(all leaves in graph); //O(n) to find
int minWeight = 0; //any zero length path is minimum weight path currently found

//note that we a going to add new elements in list during the iteration
//and we will need to iterate through all of them including new added
//so in usual programming languages you will need to use for cycle
for each (leaf in leaves)
{
       //we will have last node in tree with no connection
       //because we will delete it on previous iteration
       //and we don't need to process this node
       //for this problem it is last node, 
       //but it may be not last if we have several trees instead of one
       if (no point connected to leaf) continue; 

     n = only one node still connected to leaf       
     connection = connection between n and leaf

     //check if we can make a short path which goes through n 
     //from one already processed "child" node to another
     if (leaf.shortest_path + 
         connection.weight 
         + n.shortest_path < minWeight  )
     {
         minWeight = leaf.shortest_path+connection.weight+n.shortest_path; 
     }
     //and we need to keep lightest path from any processed "child" to n
     if (leaf.shortest_path + connection.weight < n.shortest_path )
     {
          n.shortest_path = leaf.shortest_path + connection.weight;
          if(n.shortest_path < minWeight )
          {
               minWeight = n.shortest_path;
          }
     }

     //delete connection and 
     connection.delete();
     //if n is now leaf add it to leaf list
     if (n is leaf) 
     {
         leaves.Add(n);
     }
}

所以在主循环中我们只处理了一个节点,因此我们有O(n)复杂度。 如果你需要非零长度路径但是这个算法没有找到它,则意味着所有边缘都具有正重量,最轻的非零长度路径是最轻的边缘。

答案 2 :(得分:0)

这是线性解决方案的伪代码:

res = 0 // A path from a node to itself has a weight 0

// Runs depth first search from the v vertex and returns the weight of
// the shortest path that starts in a descendant of v and ends in v
def dfs(v):
    // The shortest path from a descendant of v to v
    minLength = 0
    // The list of all lengths of paths coming from children
    childLengths = []
    for edge in edges(v):
        childLength = dfs(edge.to)
        // Update the result with a vertical path from the child
        res = min(res, childLength + edge.weight) 
        // Update the minLength based on the child's value
        minLength = min(minLength, childLength + edge.weight)
        childLengths.add(childLength + edge.weight)
    if childLengths.size() >= 2:
        // Update the result based on two smallest children values.
        // Finds the the first and the second minimum in a list in a linear time.
        min1 = firstMin(childLengths)
        min2 = secondMin(childLengths)
        res = min(res, min1 + min2)
    return minLength    

dfs(root)
return res

如果树没有根,我们可以选择任何顶点作为根。