我只是要描述一些 Prim 的实现,希望能帮到你。
首先,您的问题没有指定如何将边缘输入到程序中。您有顶点总数和这些顶点的位置。你怎么知道哪些是连接的?
假设您有边缘(以及这些边缘的权重。就像上面的@doomster 所说,它可能是点之间的平面距离,因为它们是坐标),我们可以开始考虑我们的实现。维基百科描述了三种不同的数据结构,导致三种不同的运行时间:http ://en.wikipedia.org/wiki/Prim 's_algorithm#Time_complexity
最简单的是邻接矩阵。正如您可能从名称中猜到的那样,矩阵描述了“相邻”的节点。准确地说,有|v|
行和列(其中|v|
是顶点数)。at 的值adjacencyMatrix[i][j]
因用途而异。i
在我们的例子中,它是节点和之间的边的权重(即距离)j
(这意味着您需要以某种方式索引顶点。例如,您可以将顶点添加到列表中并使用它们在列表中的位置) .
现在使用这个邻接矩阵,我们的算法如下:
- 创建一个包含所有顶点并以“距离”为键的字典。最初所有节点的距离是无穷大。
- 创建另一个字典来跟踪“父母”。我们使用它来生成 MST。跟踪边缘更自然,但通过跟踪“父母”实际上更容易实现。请注意,如果您将一棵树作为根(即指定某个节点作为根),那么每个节点(除了根)都只有一个父节点。因此,通过制作这本父母词典,我们将拥有我们的 MST!
v
使用从原始列表中
随机选择的节点创建一个新列表。
- 从距离字典中删除
v
并将其添加到父字典中,并以 null 作为其父项(即它是“根”)。
- 遍历该节点的邻接矩阵中的行。对于任何
w
已连接的节点(对于未连接的节点,您必须将其邻接矩阵值设置为某个特殊值。0、-1、int
最大值等)将其在字典中的“距离”更新为adjacencyMatrix[v][w]
. 这个想法是它不再“无限远”了......我们知道我们可以从那里到达v
。
- 虽然字典不为空(即当有我们仍然需要连接的节点时)
- 查看字典并找到距离最小的顶点
x
- 将其添加到我们的新顶点列表中
- 对于它的每个邻居,将它们的距离
min(adjacencyMatrix[x][neighbor], distance[neighbor])
更新为 并将其父节点更新为x
。基本上,如果有更快的方法可以到达,neighbor
那么距离字典应该更新以反映这一点;如果我们然后添加neighbor
到新列表中,我们知道我们实际添加了哪条边(因为父字典说它的父是x
)。
- 我们完成了。随心所欲地输出 MST(您需要的一切都包含在父母字典中)
我承认从维基百科页面到上面概述的实际实现有一点飞跃。我认为解决这个差距的最好方法就是暴力破解代码。我的意思是,如果伪代码说“找到最小 [blah] 使得 [foo] 为真”,那么编写执行该操作所需的任何代码,并将其粘贴在单独的方法中。它肯定是低效的,但它会是一个有效的实现。图算法的问题在于有 30 种方法可以实现它们,而且它们的性能都非常不同;维基百科页面只能从概念上描述算法。好消息是,一旦你实施了一些方式,您可以快速找到优化(“哦,如果我在这个单独的数据结构中跟踪这个状态,我可以让这个查找方式更快!”)。顺便说一句,this 的运行时间是O(|V|^2)
. 我懒得详细分析该分析,但大致是因为:
- 所有初始化都
O(|V|)
更糟
- 我们执行循环
O(|V|)
时间并花O(|V|)
时间查看字典以找到最小节点。所以基本上多次找到最小节点的总时间是O(|V|^2)
。
- 更新距离字典所需的时间是
O(|E|)
因为我们只处理每条边一次。因为这|E|
也是O(|V|^2)
O(|V|^2)
- 跟踪父母是
O(|V|)
- 输出树是
O(|V| + |E|) = O(|E|)
最坏的
- 添加所有这些(除了(2)之外,它们都不应该相乘)我们得到
O(|V|^2)
堆的实现与O(|E|log(|V|)
上面的非常相似。唯一的区别是更新距离O(log|V|)
不是O(1)
(因为它是一个堆),而是查找/删除 min 元素O(log|V|)
而不是O(|V|)
(因为它是一个堆)。分析中的时间复杂度非常相似,您最终会得到O(|V|log|V| + |E|log|V|) = O(|E|log|V|)
所需的结果。
实际上......我有点困惑为什么邻接矩阵实现关心它是一个邻接矩阵。它也可以使用邻接表来实现。我认为关键部分是如何存储距离。我在上面概述的实现中可能会有所偏离,但我很确定它实现 Prim 的算法满足 wikipedia 概述的时间复杂度限制。