最小生成树
一个无向图 G 的最小生成树(minimum spanning tree) 就是由该图的那些连接 G 的所有顶点的边构成的树,且总价值最低。
最小生成树存在当且仅当 G 是连通的。
Prim 算法
Prim 算法是使其连续的一步步长成。在每一步,都要把一个节点当做跟并网上加边,这样也就把相关联的顶点增长到了树上。
/**
* 类似 {@link Dijkstra} 算法。
* 每次循环都选取 Table 中值最小的边。
*/
private static <T> void find(DGraph<T> graph, Vertex<T> vertex) {
Map<Vertex<T>, TableEntity<Vertex<T>>> table = TableEntity.getTable(graph, vertex);
DGraph<T> primGraph = new ListDGraph<>();
for (int i = 0; i < graph.size(); i++) {
Vertex<T> minVertex = findUnknownMin(table);
if (minVertex == null) {
break;
}
TableEntity<Vertex<T>> minTable = table.get(minVertex);
minTable.know = true;
primGraph.add(new Vertex<>(minVertex.getValue()));
if (minTable.path != null) {
T thisValue = minVertex.getValue();
T pathValue = minTable.path.getValue();
primGraph.add(new Edge<>(new Vertex<>(thisValue), new Vertex<>(pathValue)));
primGraph.add(new Edge<>(new Vertex<>(pathValue), new Vertex<>(thisValue)));
}
if (minVertex.getEdgeList() != null) {
for (Edge<Vertex<T>> edge : minVertex.getEdgeList()) {
if (edge.getDest() != null) {
TableEntity<Vertex<T>> edgeTable = table.get(edge.getDest());
if (!edgeTable.know && edge.getWeight() < edgeTable.dist) {
edgeTable.dist = edge.getWeight();
edgeTable.path = minVertex;
}
}
}
}
}
System.out.println();
Utils.printGraph(primGraph);
}
/**
* 从未知表中读取一个 dist 最小的顶点
*/
private static <T> Vertex<T> findUnknownMin(Map<Vertex<T>, TableEntity<Vertex<T>>> table) {
int min = TableEntity.INFINITY;
Vertex<T> vertex = null;
for (Vertex<T> key : table.keySet()) {
TableEntity<Vertex<T>> item = table.get(key);
if (!item.know && min >= item.dist) {
min = item.dist;
vertex = key;
}
}
return vertex;
}
Kruskal 算法
第二种贪婪策略是连续的按照最小的权选择边,并且当所选的边不产生圈时就把它当做取定的边。
/**
* 1.新建图G,G中拥有原图中相同的节点,但没有边
* 2.将原图中所有的边按权值从小到大排序
* 3.从权值最小的边开始,如果这条边连接的两个节点于图G中不在同一个连通分量中,则添加这条边到图G中
* 4.重复3,直至图G中所有的节点都在同一个连通分量中
*/
private static <T> DGraph<T> find(DGraph<T> graph, Vertex<T> vertex) {
DGraph<T> newGraph = getNoEdgeGraph(graph);
BinaryHeap<Edge<Vertex<T>>> priorityQueue = makePriorityQueue(graph);
while (!priorityQueue.isEmpty()) {
Edge<Vertex<T>> edge = priorityQueue.deleteMin();
if (edge.getDest() != null
&& edge.getSource() != null) {
if (!connected(newGraph, edge.getDest(), edge.getSource())) {
newGraph.add(new Edge<>(edge.getDest(), edge.getSource()));
newGraph.add(new Edge<>(edge.getSource(), edge.getDest()));
}
}
}
return newGraph;
}
/**
* 通过原图的顶点重新创建一个图,但不保留其边
*/
private static <T> DGraph<T> getNoEdgeGraph(DGraph<T> graph) {
DGraph<T> newGraph = new ListDGraph<>();
Iterator<T> iterator = graph.iterator(ITERATOR_TYPE_BFS, graph.get(0).getValue());
while (iterator.hasNext()) {
newGraph.add(new Vertex<>(iterator.next()));
}
return newGraph;
}
/**
* 根据边的权值创建一个优先队列
*/
private static <T> BinaryHeap<Edge<Vertex<T>>> makePriorityQueue(DGraph<T> graph) {
BinaryHeap<Edge<Vertex<T>>> priorityQueue = new BinaryHeap<>();
for (int i = 0; i < graph.size(); i++) {
Vertex<T> ve = graph.get(i);
List<Edge<Vertex<T>>> edgeList = ve.getEdgeList();
if (edgeList != null
&& !edgeList.isEmpty()) {
for (Edge<Vertex<T>> edge : edgeList) {
priorityQueue.insert(edge);
}
}
}
return priorityQueue;
}
/**
* 当前图加上这两个顶点后是不是连通的
*/
private static <T> boolean connected(DGraph<T> graph, Vertex<T> ve1, Vertex<T> ve2) {
Iterator<T> iterator = graph.iterator(ITERATOR_TYPE_BFS, ve1.getValue());
while (iterator.hasNext()) {
if (iterator.next().equals(ve2.getValue())) {
return true;
}
}
return false;
}
上面的优先队列:BinaryHeap 可点击这里查看源码,或者点击下面的链接:
https://github.com/0xZhangKe/Algorithms/blob/master/src/com/zhangke/java/graph/adt/heap/BinaryHeap.java
更多其他算法及数据结构相关的知识可去我的 Github 仓库查看:
https://github.com/0xZhangKe/Algorithms
如果觉得还不错的话,欢迎关注我的个人公众号,我会不定期发一些干货文章~
也可以加我微信: