图论基础(中级)
引言
之前我们在图论基础的初级中学过了图的两种实现方式,介绍了关于图的两种遍历方式,一个是深度优先搜索,另一个是广度优先搜索,同时对这两种的遍历方式的应用进行了简单的介绍。
在这一章中,我们将介绍两类与图论相关的问题,一个是最小生成树问题,另一个是单源最短路径问题,这两类都是图论中经典的问题,我们在此会对他们进行详细的介绍。
带权图的定义及实现
定义
在上一章中,图都是以无权图的形式进行实现的,然后这里实现的最小生成树与最短路径的算法都是以带权图为基础,所以我们先简单介绍一下带权图。
所谓带权图,其实很简单,就是图中的每一条边上有自己的权重,简单点说就是边上有数值,这个数值就是通过这条边的费用,而且这个数值有正有负。
图示:
实现
有权图的实现很简单,只要在我们之前实现的无权图上加上一个属性值,以表示边的权重即可,这边我们实现了一个Edge类,用于表示一条边的两个端点与这条边的权重值。
代码实现:
Edge类:
public class Edge<Weight extends Number & Comparable> implements Comparable<Edge> {
private int v, w;//两个顶点,在有向图中,V表示起点,W表示终点
private Weight weight;//边的权值
public Edge(int v, int w, Weight weight) {
//用数值初始化
this.v = v;
this.w = w;
this.weight = weight;
}
public Edge(Edge<Weight> edge) {
//直接用另一条边初始化
this.v = edge.v;
this.w = edge.w;
this.weight = edge.weight;
}
public Edge() {
//无参构造器
}
public int getV() {
//获取起点
return v;
}
public int getW() {
//获取终点
return w;
}
public int other(int x) {
//给定一个顶点,返回另一个顶点
assert x == v || x == w;
return x == this.v ? this.w : this.v;
}
@Override
public String toString() {
return "Edge{" +
"v=" + v +
" -> w=" + w +
", weight=" + weight +
'}';
}
@Override
public int compareTo(Edge o) {
//比较两条边
if (this.weight.compareTo(o.weight) > 0) {
//如果该边的权值大于另一条边
return 1;
} else if (this.weight.compareTo(o.weight) < 0) {
//小于
return -1;
} else {
//等于
return 0;
}
}
public Weight getWeight() {
//获取该边的权值
return weight;
}
}
WeightGraph接口:
public interface WeightGraph<Weight extends Number & Comparable> {
int getVertexNum();//获取顶点数
int getEdgeNum();//获取边数
void addEdge(Edge<Weight> edge);//添加一条边
boolean hasEdge(int v, int w);//两个顶点间是否有边
Iterable<Edge<Weight>> adj(int v);//定点的邻边迭代器
void show();//打印图
}
带权图的邻接矩阵实现:
public class DenseWeight_Graph<Wegight extends Number & Comparable> implements WeightGraph {
private int vertexNum;//顶点数
private int edgeNum;//边数
private boolean directed;//表示是否为有向图
private Edge<Wegight>[][] g;//邻接矩阵
public DenseWeight_Graph(int vertexNum, boolean directed) {
//初始化
this.vertexNum = vertexNum;
this.directed = directed;
this.edgeNum = 0;
g = new Edge[vertexNum][vertexNum];
}
@Override
public int getVertexNum() {
//返回顶点数
return vertexNum;
}
@Override
public int getEdgeNum() {
//返回边数
return edgeNum;
}
@Override
public void addEdge(Edge edge) {
//添加一条边
assert edge != null;
assert edge.getV() >= 0 && edge.getV() < vertexNum;
assert edge.getW() >= 0 && edge.getW() < vertexNum;
if (!hasEdge(edge.getV(), edge.getW())) {
g[edge.getV()][edge.getW()] = new Edge(edge);
if (!directed && edge.getV() != edge.getW()) {
g[edge.getW()][edge.getV()] = new Edge(edge.getW(), edge.getV(), edge.getWeight());
}
edgeNum++;
}
}
@Override
public boolean hasEdge(int v, int w) {
assert v >= 0 && v < vertexNum &am