很遗憾,您的算法没有产生正确的答案。
请考虑您提供的练习表屏幕截图中的第一个示例。那里有 7 个顶点,让我们按照给定的顺序从 1 到 7 对它们进行编号。我同意望远镜网络最初是最小生成树,因为描述中说明了“所有电缆的总长度是最小的”。
以下边是我计算出的这个 MST 的边(使用建议的顶点编号):
{1,3}, {2,3}, {3,4}, {4,5}, {4,6}, {6,7}
使用您找到中心的方式,中心是顶点 4,因为每个其他顶点距它最多 2 跳。现在让我们按照您的建议将 MST 划分为其子图。有 3 个子图(我使用它们的一组顶点来识别它们):
- {1,2,3}
- {5}
- {6,7}
因此,离中心最远的顶点是
{2,5,7}
很清楚,从顶点 2 到顶点 7 的距离最高。我使用勾股定理计算出了 9.99 = sqrt(10) + 2 + sqrt(8) + 2 的距离。正如您所建议的,您的解决方案是连接顶点 2 和顶点 7 以最小化直径。
但有一个更好的解决方案:连接顶点 3 和顶点 7(如练习中给出的解决方案)。原因是从顶点 1 到顶点 7 的距离从 9.06 = sqrt(5) + 2 + sqrt(8) + 2 下降到 8.82 = sqrt(10) + sqrt(32)。使用您的解决方案,从顶点 1 到顶点 7 的最小距离仍为 9.06,这并未达到最佳效果。
其次,不需要您在第 8 步中建议的递归。它是一棵树,那么三个子图中的一个如何在不通过中心的路线上连接到另一个?如果是,则该图将包含一个循环,因为子图已经通过中心连接。
第三,在密集图上,最好使用 Prim-Dijkstra 来实现 O(E + Vlog(v)) 的 MST。它很容易实现,例如,您可以在 wikipedia 上查找它。不过我想你已经知道了。
最后,如何产生正确的结果?
如您所见,如何使用启发式方法找到边缘并不明显,因为您的想法没有成功。
什么是天真的解决方案?
您几乎可以选择每个顶点对,即 O(n^2) 多个候选边。要(天真地)计算它的直径,您需要 O(n^2)。因此,您可以尝试每条边并计算插入此附加边的树的直径。最后,您输出最小直径。这将是 O(n^2 * n^2) = O(n^4)。
我们怎样才能做得更好?我喜欢先考虑简单的解决方案,因为您会知道需要查看哪些内容。从那里您开始删除不必要的操作。您正确发现的是最长的路径需要变短(否则直径不会改变)。换句话说,您添加的边必须连接位于最长路径上的两个顶点。但是你不知道是哪两个。您可以轻松地构建每个顶点都是最长路径的一部分的示例,但是对于分布良好的望远镜网络,最长路径上的顶点要少得多。现在您只尝试沿最长路径插入边并在 O(n^2) 中再次计算新直径。如果你在最长的路径上有 k 个顶点,你会得到 O(n^2 k^2)。
总结:
- 使用 Prim 算法计算生成树 -> O(n^2) 在您的完整图上
- 计算树的最长路径,即直径 -> O(n^2) 得到最长路径上的所有 k 个顶点
- 尝试在这条最长路径上的所有顶点对之间一次添加一个 (O(k^2) many) 边,并存储所有直径的最小值,即您必须一次又一次地重新计算直径 ( O(n^2))。总计:O(n^2 k^2)
如果我们能改进计算直径的时间就好了。对于在线性时间内起作用的树,请参阅Linear algorithm of finding tree diameter。您的图表最初是一棵树,但您一次最多添加一条边,即它仍然几乎是一棵树。因此,您需要做的就是找到原始树中最远的两个节点 u1、v1,然后在我们的算法中插入一条边 e={u,v} 时,您删除一条不同的边比顶点 u 处的 e ,这样循环就消失了,你又得到了一棵树。现在您再次找到最远的顶点 u2、v2。现在您计算四个顶点 u1、u2、v1、v2 之间的最大距离,这是您在线性时间内的直径。
这会将您的运行时间缩短到 O(n*k^2 + n^2)