本文是对 Swift Algorithm Club 翻译的一篇文章。
Swift Algorithm Club是 raywenderlich.com网站出品的用Swift实现算法和数据结构的开源项目,目前在GitHub上有18000+⭐️,我初略统计了一下,大概有一百左右个的算法和数据结构,基本上常见的都包含了,是iOSer学习算法和数据结构不错的资源。
🐙andyRon/swift-algorithm-club-cn是我对Swift Algorithm Club,边学习边翻译的项目。由于能力有限,如发现错误或翻译不妥,请指正,欢迎pull request。也欢迎有兴趣、有时间的小伙伴一起参与翻译和学习🤓。当然也欢迎加⭐️,🤩🤩🤩🤨🤪。
本文的翻译原文和代码可以查看🐙swift-algorithm-club-cn/Minimum Spanning Tree(Unweighted))
最小生成树(未加权图)(Minimum Spanning Tree (Unweighted Graph))
最小生成树描述了包含访问图中每个节点所需的最小数目边的路径。
看下图:
如果我们从节点a
开始并想要访问每个其他节点,那么最有效的路径是什么? 我们可以使用最小生成树算法来计算它。
这是图的最小生成树。 它由粗体边表示:
绘制为更一般形式的树,它看起来像这样:
要计算未加权图上的最小生成树,我们可以使用广度优先搜索 算法。广度优先搜索从源节点开始,并在移动到下一级邻居之前首先通过探索直接邻居节点来遍历图。如果我们通过选择性地删除边来调整此算法,那么它可以将图转换为最小生成树。
让我们逐步完成这个例子。 我们从源节点a
开始,将其添加到队列中并将其标记为已访问。
1 | queue.enqueue(a) |
队列现在是[a]
。 与广度优先搜索一样,我们将队列前面的节点a
出队,并将其直接邻居节点b
和h
入队。 将它们标记为访问过。
1 | queue.dequeue() // a |
队列现在是[b, h]
。 将b
出队并将邻居节点c
入队。 将其标记为已访问。 将b
到h
边移除,因为已经访问过h
。
1 | queue.dequeue() // b |
队列现在是[h, c]
。 将h
出队并将邻居节点g
和i
入队,并将它们标记为已访问。
1 | queue.dequeue() // h |
队列现在是[c, g, i]
。 将c
出队并将邻居节点d
和f
入队,并将它们标记为已访问。 删除c
和i
之间的边,因为已经访问了i
。
1 | queue.dequeue() // c |
队列现在是[g, i, d, f]
。 出队g
。 它的所有邻居都已经被发现了,所以没有什么可以入队的。 删除g
到f
的边,以及g
到i
的边,因为已经发现了f
和i
。
1 | queue.dequeue() // g |
队列现在是[i, d, f]
。 出队i
。 这个节点没有别的办法。
1 | queue.dequeue() // i |
队列现在是[d, f]
。 将d
出队并将邻居节点e
入队。 将其标记为已访问。 删除d
到f
的边,因为已经访问了f
。
1 | queue.dequeue() // d |
队列现在是[f, e]
。 出列f
。 删除f
和e
之间的边,因为已经访问过e
。
1 | queue.dequeue() // f |
队列现在是[e]
。 出队e
。
1 | queue.dequeue() // e |
队列为空,这意味着已计算出最小生成树。
代码如下:
1 | func breadthFirstSearchMinimumSpanningTree(graph: Graph, source: Node) -> Graph { |
此函数返回一个新的Graph
对象,该对象仅描述最小生成树。 在图中,这将是仅包含粗体边的图。
将此代码放在playground上,进行测试:
1 | let graph = Graph() |
注意: 在未加权的图上,任何生成树始终是最小生成树。 这意味着您还可以使用深度优先搜索来查找最小生成树。
作者:Chris Pilcher, Matthijs Hollemans
翻译:Andy Ron
校对:Andy Ron