算法笔记: 最小生成树

最小生成树,是一个十分重要的知识点。最早学它的时候,觉得它的算法思想很朴素,实现起来也很简单,但是伴随着后来的学习,我发现,最小生成树算法确实十分常用,算法题中也常常会手撸一个最小生成树,在这里,在把最小生成树相关知识点整理一下。

定义

最小生成树,首先,它是一棵基于图生成的树。它符合树的定义,并且包含了原图中的所有节点。同时我们要求,这棵树,其边相连的权重和是所有生成树中最小的,这就是最小生成树。

应用

最小生成树的应用十分广泛,我在各种与图相关的应用场景都见过,比如计算机网络,决策树,金融数据分析等。

贪婪算法

我们首先看一下最小生成树算法的一个基本思路。

原文中讲的有些复杂,其实原理韩式比较简单的。

首先,我们认为所有的初始边都是灰色的,之后我们依次选取灰色边中最小的变成黑边,这个黑边还要满足连接的两个点不能是之前已经被黑边连接过的。假设一共有n个点,我们执行上述过程n-1次,就得到了一棵最小生成树。

后续实现中用到的API

public class Edge implements Comparable<Edge>
{
 private final int v, w;
 private final double weight;
 public Edge(int v, int w, double weight)
 {
 this.v = v;
 this.w = w;
 this.weight = weight;
 }
 public int either()
 { return v; }
 public int other(int vertex)
 {
 if (vertex == v) return w;
 else return v;
 }
 public int compareTo(Edge that)
 {
 if (this.weight < that.weight) return -1;
 else if (this.weight > that.weight) return +1;
 else return 0;
 }
}
public class EdgeWeightedGraph
{
 private final int V;
 private final Bag<Edge>[] adj;
 public EdgeWeightedGraph(int V)
 {
 this.V = V;
 adj = (Bag<Edge>[]) new Bag[V];
 for (int v = 0; v < V; v++)
 adj[v] = new Bag<Edge>();
 }
 public void addEdge(Edge e)
 {
 int v = e.either(), w = e.other(v);
 adj[v].add(e);
 adj[w].add(e);
 }
 public Iterable<Edge> adj(int v)
 { return adj[v]; }
}

Kruskal's algorithm

算法超简单啊,和上面提到的贪婪算法如出一辙。每次挑选权重最小的边,只要不形成环,就把这条边加进入,直到形成树为止。实现的时候最大的困难就在于如何判断会不会形成环,这个问题在算法的第一讲就已经讲到过了,我们可以使用并查集,来完成这个操作。

public class KruskalMST
{
 private Queue<Edge> mst = new Queue<Edge>();
 public KruskalMST(EdgeWeightedGraph G)
 {
 MinPQ<Edge> pq = new MinPQ<Edge>();
 for (Edge e : G.edges())
 pq.insert(e);
 UF uf = new UF(G.V());
 while (!pq.isEmpty() && mst.size() < G.V()-1)
 {
 Edge e = pq.delMin();
 int v = e.either(), w = e.other(v);
 if (!uf.connected(v, w))
 {
 uf.union(v, w);
 mst.enqueue(e);
 }
 }
 }
 public Iterable<Edge> edges()
 { return mst; }
}

具体的题目应用可以看我的另一篇博客数据中心

算法复杂度为ElogE

发布了85 篇原创文章 · 获赞 100 · 访问量 13万+

猜你喜欢

转载自blog.csdn.net/caozixuan98724/article/details/89362874
今日推荐