【问题标题】:Find shortest set of paths connecting all nodes找到连接所有节点的最短路径集
【发布时间】:2020-10-04 12:13:23
【问题描述】:
我在二维坐标空间中有一组点。
我想找到以最短总长度连接它们的一组路径。 (启发式解决方案可以,不需要精确。)
这听起来像是旅行商问题,但它是不同的。我不是在寻找一个循环访问每个点一次且仅一次。我只需要将每个点连接到至少一个其他点,以便集合中的所有点至少间接地相互连接,并使所选连接的长度总和最小化。因此,它应该是非循环的,以最小化连接长度的总和。
简单的最近邻算法(即,将每个点连接到其尚未连接的最近邻)不起作用,因为彼此相距很远的小集群最终会被隔离,而你总是会结束向上创建循环。
【问题讨论】:
标签:
algorithm
search
optimization
graph-theory
shortest-path
【解决方案1】:
就像其他人指出的那样,您需要构建 MST。但是,如果您想从所有点对(MST 的潜在边缘)中进行选择,那么您最终需要处理大量边缘,这会破坏运行时间和内存。
从任何点开始可能会更快,将其连接到不创建循环的最近邻居并重复此步骤直到所有内容都连接。
这是另一种更不常见的构建最小生成树的方法,它应该在您的应用程序中更快。
如果你愿意,我可以(尝试)证明我刚刚提出的算法。
如果您针对您的问题得出不同的解决方案,请注意 MST 不一定是唯一的,可能有多个最佳解决方案。
【解决方案2】:
如果我对您的问题的理解正确,那么您正在寻找的是最小生成树(通常缩写为 MST)。 MST 只是路径的子集,
- (a) 连接所有点,
- (b) 这样做时总长度可能最短,并且
- (c) 没有循环。
有几种众所周知的算法可以找到 MST,例如 Prim 算法和 Kruskal 算法——我将带您了解 Kruskal 算法,我个人认为这是最直观的。如果您有兴趣,您应该能够在线或在算法教科书中找到其他算法。
Kruskal 的开始是按长度对各个路径进行排序。使用该列表,我们可以通过重复以下过程来创建 MST,直到所有点/顶点都包含在我们的树中:
- 考虑列表中的最短路径。
- 如果它会在您的 MST 中创建一个循环,请不要将其添加到树中(只需将其从列表中删除)。
- 否则,将其添加到树中(并将其从列表中删除)。
最终,你会得到一棵树,
- (a) 连接所有点 — 保证由于考虑了每条路径,因此符合 MST 条件的每个顶点都必须在路径上,并且添加路径以到达先前未连接的顶点不能创建循环;
- (b) 具有可能的最低总长度 — 保证因为路径是按长度升序添加的;和
- (c) 没有循环 - 因为算法明确避免了这种情况,所以可以保证。
注意事项:
(1) 在处理 MST(和其他图问题)时,我们经常用特定的名称来称呼部件:点的集合是一个“图”,一个特定的点或节点是一个“顶点”,顶点之间的路径是“边”,边的长度是“权重”。
(2) Kruskal 算法的运行时间为 O(ElogV),其中 E 是路径/边的数量,V 是点/顶点的数量。
(3) 如果您的图有任何顶点或一组顶点与数据集的其余部分断开连接,那么您最终会在“MST 森林”中得到多个 MST。
(4) 一个图可以有多个 MST;例如在this graph中,MST的最小长度是6——这是由以下两个MST实现的:MST1,MST2
(5) 确定添加路径是否会创建循环实际上可能很棘手;一种方法是使用 UnionFind 数据结构,您可以使用 here。
【解决方案3】:
你描述的问题听起来像克鲁斯卡尔算法:
Kruskal 算法是一种最小生成树算法,它找到
连接任意两棵树的权重最小的边
森林。它是图论中的一种贪心算法,因为它找到了一个
连接加权图的最小生成树增加增加
每一步的成本弧。这意味着它找到了边的一个子集
形成一棵包含每个顶点的树,其中总权重
树中所有边的最小化。如果图表不是
连接,然后它找到一个最小跨越森林(最小跨越
每个连接组件的树)。
Kruskal 时间复杂度最坏的情况是 O(E log E),这是因为我们需要对边进行排序。 Prim 时间复杂度最坏的情况是 O(E log V) 与优先级队列,甚至更好,O(E+V log V) 与 Fibonacci Heap。当图形稀疏时,您应该使用 Kruskal,即少量边,如 E=O(V),当边已经排序或者我们可以在线性时间内对它们进行排序。我们应该在图形密集时使用 Prim,即边数很高,例如 E=O(V²)