目录
- 无权图
- 两种底层实现
- 稠密图-基于矩阵
- 稀疏图-基于多个动态数组
- 连通分量
- 寻找路径
- 深度优先寻找路径
- 广度优先寻找路径(最短路径)
- 两种底层实现
- 带权图
- 两种底层实现
- 稠密图
- 稀疏图
- 最小生成树
- Prim算法
- Kruskal算法
- 最短路径
- ??
- ??
- 两种底层实现
无权图
import java.util.Iterator;
public interface Graph {
/**
* 返回顶点数
* @return
*/
int pointCount();
/**
* 返回边数
* @return
*/
int edgeCount();
/**
* 为两个顶点添加一条边
* @param pointA
* @param pointB
*/
void addEdge(int pointA, int pointB);
/**
* 判断两个顶点间是否有边
* @param pointA
* @param pointB
* @return
*/
boolean hasEdge(int pointA, int pointB);
/**
* 通过指定顶点遍历与其相连顶点的迭代器
* @param point
* @return
*/
Iterator<Integer> pointIterator(int point);
}
两种底层实现
基于矩阵的稠密图
import java.util.Iterator;
/**
* 稠密图
* 基于n*n矩阵
*/
public class DenseGraph implements Graph {
/**
* 顶点数
*/
private int pointCount;
/**
* 边数
*/
private int edgeCount;
/**
* 是否是有向图
* true 表示有向图; false 表示无向图
*/
private boolean directed;
/**
* n*n矩阵
*/
private boolean[][] data;
/**
* 构造函数
* @param pointCount 定点数
* @param directed 是否有向图
*/
public DenseGraph(int pointCount, boolean directed) {
assert pointCount > 0;
this.pointCount = pointCount;
this.edgeCount = 0;
this.directed = directed;
this.data = new boolean[pointCount][pointCount]; // 默认都是false,表示没有边
}
@Override
public int pointCount() {
return this.pointCount;
}
@Override
public int edgeCount() {
return this.edgeCount;
}
@Override
public void addEdge(int pointA, int pointB) {
if (hasEdge(pointA, pointB)) {
return;
}
data[pointA][pointB] = true;
this.edgeCount++;
if (!this.directed) {
// 如果是无向图,需要指定对称矩阵的另一部分的值
data[pointB][pointA] = true;
}
}
@Override
public boolean hasEdge(int pointA, int pointB) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
return data[pointA][pointB];
}
@Override
public Iterator<Integer> pointIterator(int point) {
assert point >= 0 && point < this.pointCount;
return new Iterator<Integer>() {
private int index = -1;
@Override
public boolean hasNext() {
boolean[] allEdge = data[point];
while (index + 1 < pointCount()) {
index++;
if (allEdge[index]) {
return true;
}
}
return false;
}
@Override
public Integer next() {
return index;
}
};
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
.append(" has ").append(this.pointCount).append(" " + "points, ")
.append(this.edgeCount).append(" edges.").append("\n");
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
for (boolean[] line : data) {
for (boolean edge : line) {
stringBuilder.append(edge ? 1 : 0).append(' ');
}
stringBuilder.append("\n");
}
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
return stringBuilder.toString();
}
}
基于多个动态数组的稀疏图
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 稀疏图
* 基于n条动态数组
*/
public class SparseGraph implements Graph {
/**
* 顶点数
*/
private int pointCount;
/**
* 边数
*/
private int edgeCount;
/**
* 是否是有向图
* true 表示有向图; false 表示无向图
*/
private boolean directed;
/**
* n个动态数组
*/
private Object[] data;
public SparseGraph(int pointCount, boolean directed) {
assert pointCount > 0;
this.pointCount = pointCount;
this.edgeCount = 0;
this.directed = directed;
this.data = new Object[pointCount];
for (int i = 0; i < this.data.length; i++) {
this.data[i] = new ArrayList<Integer>();
}
}
@Override
public int pointCount() {
return this.pointCount;
}
@Override
public int edgeCount() {
return this.edgeCount;
}
@Override
public void addEdge(int pointA, int pointB) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
// 支持两个顶点间存在多个边
((List<Integer>) this.data[pointA]).add(pointB);
if (pointA != pointB && !directed) {
((List<Integer>) this.data[pointB]).add(pointA);
}
}
@Override
public boolean hasEdge(int pointA, int pointB) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
return ((List<Integer>) this.data[pointA]).contains(pointB);
}
@Override
public Iterator<Integer> pointIterator(int point) {
assert point >= 0 && point < this.pointCount;
return ((List<Integer>) this.data[point]).iterator();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
.append(" has ").append(this.pointCount).append(" " + "points, ")
.append(this.edgeCount).append(" edges.").append("\n");
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
for (int i = 0; i < data.length; i++) {
stringBuilder.append("point ").append(i).append(" : ").append(data[i]).append("\n");
}
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
return stringBuilder.toString();
}
}
连通分量
import java.util.Arrays;
import java.util.Iterator;
/**
* 连通分量
*/
public class ConnectedComponents {
private Graph graph;
/**
* 顶点是否访问过
*/
private boolean[] visited;
/**
* 顶点所属分组
* 值相等的顶点是连通的
*/
private int[] id;
/**
* 连通分量个数
*/
private int connectedComponents;
public ConnectedComponents(Graph graph) {
this.graph = graph;
int pointCount = this.graph.pointCount();
this.visited = new boolean[pointCount]; // 默认false都没访问过
this.id = new int[pointCount];
Arrays.fill(this.id, -1);
this.connectedComponents = 0;
// 深度遍历所有顶点
for (int point = 0; point < pointCount; point++) {
if (!visited[point]) {
dfs(point);
connectedComponents++;
}
}
}
private void dfs(int point) {
// 将该顶点的访问状态置为已访问
visited[point] = true;
// 将该顶点置为指定分组
id[point] = connectedComponents;
// 遍历与该顶点相连的所有点
Iterator<Integer> pointIterator = graph.pointIterator(point);
while (pointIterator.hasNext()) {
int next = pointIterator.next();
if (!visited[next]) {
dfs(next);
}
}
}
/**
* 返回连通分量个数
* @return
*/
public int connectedComponents() {
return this.connectedComponents;
}
/**
* 判断两个顶点是否相连
* @param pointA
* @param pointB
* @return
*/
public boolean isConnected(int pointA, int pointB) {
int pointCount = graph.pointCount();
assert pointA >= 0 && pointA < pointCount;
assert pointB >= 0 && pointB < pointCount;
return id[pointA] == id[pointB];
}
}
深度优先找路径
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
import java.util.Stack;
/**
* 深度优先遍历
*/
public class DepthFirstSearch {
private Graph graph;
/**
* 起始顶点
*/
private int source;
/**
* 维护一份映射关系 当前顶点索引 -> 前一个顶点索引
*/
private int[] fromIndex;
public DepthFirstSearch(Graph graph, int source) {
int pointCount = graph.pointCount();
assert source >= 0 && source < pointCount;
this.graph = graph;
this.source = source;
this.fromIndex = new int[pointCount];
Arrays.fill(this.fromIndex, -1);
dfs(source);
}
private void dfs(int point) {
Iterator<Integer> pointIterator = graph.pointIterator(point);
while (pointIterator.hasNext()) {
int next = pointIterator.next();
if (this.source != next && fromIndex[next] == -1) {
fromIndex[next] = point;
dfs(next);
}
}
}
public boolean hasPath(int target) {
assert target >= 0 && target < this.graph.pointCount();
return this.fromIndex[target] != -1;
}
public List<Integer> path(int target) {
if (!hasPath(target)) {
return Arrays.asList();
}
Stack<Integer> stack = new Stack<>();
int fromIndex = target;
while (fromIndex != -1) {
stack.push(fromIndex);
fromIndex = this.fromIndex[fromIndex];
}
List<Integer> path = new ArrayList<>();
while (!stack.isEmpty()) {
path.add(stack.pop());
}
return path;
}
public String showPath(int target) {
if (!hasPath(target)) {
return "the path from " + this.source + " to " + target + " not exists.";
}
List<Integer> path = path(target);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("the path from ").append(this.source).append(" to ").append(target).append(" is: ");
for (int i = 0; i < path.size(); i++) {
stringBuilder.append(path.get(i));
if (i < path.size() - 1) {
stringBuilder.append(" -> ");
}
}
return stringBuilder.toString();
}
}
广度优先找路径(无权图的最短路径)
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Queue;
import java.util.Stack;
/**
* 广度优先遍历
*/
public class BreadthFirstSearch {
private Graph graph;
/**
* 起始顶点
*/
private int source;
/**
* 维护一份映射关系 当前顶点索引 -> 前一个顶点索引
*/
private int[] fromIndex;
/**
* 相对于起始顶点的层数
*/
private int[] level;
public BreadthFirstSearch(Graph graph, int source) {
int pointCount = graph.pointCount();
assert source >= 0 && source < pointCount;
this.graph = graph;
this.source = source;
this.fromIndex = new int[pointCount];
Arrays.fill(this.fromIndex, -1);
this.level = new int[pointCount];
Arrays.fill(this.level, -1);
this.level[source] = 0;
bfs(source);
}
private void bfs(int source) {
Queue<Integer> queue = new LinkedList<>();
queue.add(source);
while (!queue.isEmpty()) {
int poll = queue.poll();
Iterator<Integer> pointIterator = this.graph.pointIterator(poll);
while (pointIterator.hasNext()) {
int next = pointIterator.next();
if (next != source && this.fromIndex[next] == -1) {
this.fromIndex[next] = poll;
this.level[next] = this.level[poll] + 1;
queue.add(next);
}
}
}
}
public boolean hasPath(int target) {
assert target >= 0 && target < this.graph.pointCount();
return fromIndex[target] != -1;
}
public List<Integer> path(int target) {
if (!hasPath(target)) {
return Arrays.asList();
}
Stack<Integer> stack = new Stack<>();
int point = target;
while (point != -1) {
stack.push(point);
point = this.fromIndex[point];
}
List<Integer> path = new ArrayList<>();
while (!stack.isEmpty()) {
path.add(stack.pop());
}
return path;
}
public int level(int target) {
assert target >= 0 && target < this.graph.pointCount();
return this.level[target];
}
public String showPath(int target) {
if (!hasPath(target)) {
return "the path from " + this.source + " to " + target + " not exists.";
}
List<Integer> path = path(target);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("the path from ").append(this.source).append(" to ").append(target).append(" is: ");
for (int i = 0; i < path.size(); i++) {
stringBuilder.append(path.get(i));
if (i < path.size() - 1) {
stringBuilder.append(" -> ");
}
}
return stringBuilder.toString();
}
}
带权图
import java.util.Iterator;
/**
* 加权图
*/
public interface WeightedGraph {
/**
* 返回顶点数
* @return
*/
int pointCount();
/**
* 返回边数
* @return
*/
int edgeCount();
/**
* 为两个顶点添加一条边
* @param pointA
* @param pointB
* @param weight
*/
void addEdge(int pointA, int pointB, double weight);
/**
* 判断两个顶点间是否有边
* @param pointA
* @param pointB
* @return
*/
boolean hasEdge(int pointA, int pointB);
/**
* 通过指定顶点遍历与其相连顶点的迭代器
* @param point
* @return
*/
Iterator<Edge> pointIterator(int point);
}
/**
* 边
*/
class Edge {
private int pointA;
private int pointB;
private double weight;
public Edge(int pointA, int pointB, double weight) {
this.pointA = pointA;
this.pointB = pointB;
this.weight = weight;
}
/**
* 顶点A
* @return
*/
public int pointA() {
return this.pointA;
}
/**
* 顶点B
* @return
*/
public int pointB() {
return this.pointB;
}
/**
* 权重
* @return
*/
public double weight() {
return this.weight;
}
/**
* 返回另一个顶点
* @param point
* @return
*/
public int otherPoint(int point) {
assert point == this.pointA || point == this.pointB;
return point == this.pointA ? this.pointB : this.pointA;
}
@Override
public String toString() {
return pointA + "--" + weight + "-->" + pointB;
}
}
带权图的两种底层实现
稠密带权图
import java.util.Iterator;
import java.util.Objects;
/**
* 稠密图
* 基于n*n矩阵
*/
public class WeightedDenseGraph implements WeightedGraph {
/**
* 顶点数
*/
private int pointCount;
/**
* 边数
*/
private int edgeCount;
/**
* 是否是有向图
* true 表示有向图; false 表示无向图
*/
private boolean directed;
/**
* n*n矩阵
*/
private Edge[][] data;
/**
* 构造函数
* @param pointCount 顶点数
* @param directed 是否有向图
*/
public WeightedDenseGraph(int pointCount, boolean directed) {
assert pointCount > 0;
this.pointCount = pointCount;
this.edgeCount = 0;
this.directed = directed;
this.data = new Edge[pointCount][pointCount]; // 默认都是NULL,表示没有边
}
@Override
public int pointCount() {
return this.pointCount;
}
@Override
public int edgeCount() {
return this.edgeCount;
}
@Override
public void addEdge(int pointA, int pointB, double weight) {
if (hasEdge(pointA, pointB)
&& data[pointA][pointB].weight() == weight) {
return;
}
data[pointA][pointB] = new Edge(pointA, pointB, weight);
this.edgeCount++;
if (!this.directed) {
// 如果是无向图,需要指定对称矩阵的另一部分的值
data[pointB][pointA] = new Edge(pointB, pointA, weight);
}
}
@Override
public boolean hasEdge(int pointA, int pointB) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
return Objects.nonNull(data[pointA][pointB]);
}
@Override
public Iterator<Edge> pointIterator(int point) {
assert point >= 0 && point < this.pointCount;
return new Iterator<Edge>() {
private int index = -1;
private Edge[] allEdge = data[point];
@Override
public boolean hasNext() {
while (index + 1 < pointCount()) {
index++;
if (Objects.nonNull(allEdge[index])) {
return true;
}
}
return false;
}
@Override
public Edge next() {
return allEdge[index];
}
};
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
.append(" has ").append(this.pointCount).append(" " + "points, ")
.append(this.edgeCount).append(" edges.").append("\n");
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
for (Edge[] line : data) {
for (Edge edge : line) {
stringBuilder.append(Objects.nonNull(edge) ? edge.weight() : "NULL").append(' ');
}
stringBuilder.append("\n");
}
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
return stringBuilder.toString();
}
}
稀疏带权图
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* 稀疏图
* 基于n条动态数组
*/
public class WeightedSparseGraph implements WeightedGraph {
/**
* 顶点数
*/
private int pointCount;
/**
* 边数
*/
private int edgeCount;
/**
* 是否是有向图
* true 表示有向图; false 表示无向图
*/
private boolean directed;
/**
* n个动态数组
*/
private List<Edge>[] data;
public WeightedSparseGraph(int pointCount, boolean directed) {
assert pointCount > 0;
this.pointCount = pointCount;
this.edgeCount = 0;
this.directed = directed;
this.data = new List[pointCount];
for (int i = 0; i < this.data.length; i++) {
this.data[i] = new ArrayList<>();
}
}
@Override
public int pointCount() {
return this.pointCount;
}
@Override
public int edgeCount() {
return this.edgeCount;
}
@Override
public void addEdge(int pointA, int pointB, double weight) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
// 支持两个顶点间存在多个边
this.data[pointA].add(new Edge(pointA, pointB, weight));
this.edgeCount++;
if (pointA != pointB && !directed) {
this.data[pointB].add(new Edge(pointB, pointA, weight));
}
}
@Override
public boolean hasEdge(int pointA, int pointB) {
assert pointA >= 0 && pointA < this.pointCount;
assert pointB >= 0 && pointB < this.pointCount;
return this.data[pointA].contains(pointB);
}
@Override
public Iterator<Edge> pointIterator(int point) {
assert point >= 0 && point < this.pointCount;
return this.data[point].iterator();
}
@Override
public String toString() {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append(directed ? "Directed" : "UnDirected").append(' ').append(this.getClass().getSimpleName())
.append(" has ").append(this.pointCount).append(" " + "points, ")
.append(this.edgeCount).append(" edges.").append("\n");
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
for (int i = 0; i < data.length; i++) {
stringBuilder.append("point ").append(i).append(" : ").append(data[i]).append("\n");
}
for (int i = 0; i < this.pointCount; i++) {
stringBuilder.append("~@");
}
stringBuilder.append("\n");
return stringBuilder.toString();
}
}
最小生成树
import java.util.List;
/**
* 最小生成树
*/
public interface MinimumSpanningTree {
/**
* 获取某带权图的最小生成树
* @return
*/
List<Edge> mst();
/**
* 最小生成树相应的权值
* @return
*/
double mstWeight();
}
Prim算法实现最小生成树
理论基础:切分定理
切分定理:给定任意切分,横切边中权值最小的边必然属于最小生成树
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
/**
* 最小生成树
* Minimum Spanning Tree
*/
public class LazyPrimMST {
/**
* 带权图
*/
private WeightedGraph graph;
/**
* 存储已遍历边的小顶堆
*/
private PriorityQueue<Edge> minHeap;
/**
* 顶点的访问情况
*/
private boolean[] visited;
/**
* 最小生成树
*/
private List<Edge> mst;
/**
* 最小生成树的权重
*/
private double mstWeight;
public LazyPrimMST(WeightedGraph graph) {
int pointCount = graph.pointCount();
this.graph = graph;
this.minHeap = new PriorityQueue<>((e1, e2) -> {
if (e1.weight() == e2.weight()) {
return 0;
}
if (e1.weight() > e2.weight()) {
return 1;
}
return -1;
});
this.visited = new boolean[pointCount]; // 默认都未访问过
this.mst = new ArrayList<>(pointCount - 1); // 最小生成树的边数为顶点数减一
this.mstWeight = 0.0D;
visit(0);
while (!this.minHeap.isEmpty()) {
Edge edge = this.minHeap.poll();
if (visited[edge.pointA()] == !visited[edge.pointB()]) {
this.mst.add(edge);
if (visited[edge.pointA()]) {
visit(edge.pointB());
} else {
visit(edge.pointA());
}
}
}
for (Edge edge : this.mst) {
this.mstWeight = new BigDecimal(this.mstWeight).add(new BigDecimal(edge.weight()))
.setScale(2, BigDecimal.ROUND_DOWN)
.doubleValue();
}
}
private void visit(int point) {
visited[point] = true;
Iterator<Edge> pointIterator = this.graph.pointIterator(point);
while (pointIterator.hasNext()) {
Edge edge = pointIterator.next();
if (visited[edge.otherPoint(point)]) {
continue;
}
this.minHeap.add(edge);
}
}
public List<Edge> mst() {
return this.mst;
}
public double mstWeight() {
return this.mstWeight;
}
}
优化后的Prim算法,需要用到索引堆(JAVA)
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import com.cedar.algorithms.heap.IndexHeap;
/**
* 最小生成树
* Minimum Spanning Tree
*/
public class PrimMST implements MinimumSpanningTree {
/**
* 带权图
*/
private WeightedGraph graph;
/**
* 存储已遍历边的小顶堆
*/
private IndexHeap<Edge> minHeap;
/**
* 边比较器
*/
private Comparator<Edge> edgeComparator;
/**
* 顶点的访问情况
*/
private boolean[] visited;
/**
* 最小生成树
*/
private List<Edge> mst;
/**
* 最小生成树的权重
*/
private double mstWeight;
public PrimMST(WeightedGraph graph) {
int pointCount = graph.pointCount();
this.graph = graph;
this.edgeComparator = (e1, e2) -> {
if (e1.weight() == e2.weight()) {
return 0;
}
if (e1.weight() > e2.weight()) {
return 1;
}
return -1;
};
// 需要一个小顶堆
this.minHeap = new IndexHeap<>(pointCount, Collections.reverseOrder(this.edgeComparator));
this.visited = new boolean[pointCount]; // 默认都未访问过
this.mst = new ArrayList<>(pointCount - 1); // 最小生成树的边数为顶点数减一
this.mstWeight = 0.0D;
visit(0);
while (!this.minHeap.isEmpty()) {
int edgeIndex = this.minHeap.extractTopIndex();
this.mst.add(this.minHeap.getElement(edgeIndex));
visit(edgeIndex);
}
for (Edge edge : this.mst) {
this.mstWeight = new BigDecimal(this.mstWeight).add(new BigDecimal(edge.weight()))
.setScale(2, BigDecimal.ROUND_DOWN)
.doubleValue();
}
}
private void visit(int point) {
visited[point] = true;
Iterator<Edge> pointIterator = this.graph.pointIterator(point);
while (pointIterator.hasNext()) {
Edge edge = pointIterator.next();
int otherPoint = edge.otherPoint(point);
if (visited[otherPoint]) {
continue;
}
Edge heapEdge = this.minHeap.getElement(otherPoint);
if (heapEdge == null) {
this.minHeap.insert(otherPoint, edge);
} else if (this.edgeComparator.compare(edge, heapEdge) < 0) {
this.minHeap.change(otherPoint, edge);
}
}
}
@Override
public List<Edge> mst() {
return this.mst;
}
@Override
public double mstWeight() {
return this.mstWeight;
}
}
Kruskal算法实现最小生成树
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.PriorityQueue;
import com.cedar.algorithms.uf.UnionFind;
import com.cedar.algorithms.uf.UnionFindBaseSizePathCompressionV1;
public class KruskalMST implements MinimumSpanningTree {
/**
* 带权图
*/
private WeightedGraph graph;
/**
* 存储所有边的小顶堆
*/
private PriorityQueue<Edge> minHeap;
/**
* 边比较器
*/
private Comparator<Edge> edgeComparator;
/**
* 并查集
*/
private UnionFind unionFind;
/**
* 最小生成树
*/
private List<Edge> mst;
/**
* 最小生成树的权重
*/
private double mstWeight;
public KruskalMST(WeightedGraph graph) {
int pointCount = graph.pointCount();
int edgeCount = graph.edgeCount();
this.graph = graph;
this.unionFind = new UnionFindBaseSizePathCompressionV1(pointCount); // 并查集
this.mst = new ArrayList<>(pointCount - 1);
this.mstWeight = 0.0D;
this.edgeComparator = (e1, e2) -> {
if (e1.weight() == e2.weight()) {
return 0;
}
if (e1.weight() > e2.weight()) {
return 1;
}
return -1;
};
this.minHeap = new PriorityQueue<>(edgeCount, this.edgeComparator);
for (int i = 0; i < pointCount; i++) {
Iterator<Edge> edgeIterator = this.graph.pointIterator(i);
while (edgeIterator.hasNext()) {
Edge next = edgeIterator.next();
// 注意,这里只处理无向图
if (next.pointA() < next.pointB()) {
this.minHeap.add(next);
}
}
}
while (!this.minHeap.isEmpty()) {
Edge poll = this.minHeap.poll();
// 使用并查集
if (this.unionFind.isConnected(poll.pointA(), poll.pointB())) {
continue;
}
this.mst.add(poll);
this.unionFind.union(poll.pointA(), poll.pointB());
}
for (Edge edge : this.mst) {
this.mstWeight = new BigDecimal(Double.toString(this.mstWeight))
.add(new BigDecimal(Double.toString(edge.weight()))).doubleValue();
}
}
@Override
public List<Edge> mst() {
return this.mst;
}
@Override
public double mstWeight() {
return this.mstWeight;
}
}