第四章 图和最小生成路径
最小生成树
每个图有很多边 下面定义边 两个点v,w 以及其长度weight
//相关部分代码
//边的相关实现 两个点以及边长
public class Edge implements Compararble<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 EdgeWeightGraph
{
private final int vertex;
private final Bag<Edge>[] adj;
public EdgeWeightGraph(int vertex){
this.vertex=vertex;
adj=(Bag<Edge>[]) new Bag[V];
for(int v=0;v<vertex;v++){
adj[v]=new Bag<Edge>();
}
}
//添加一条边
public void addEdge(Edge e){
int v=e.either(),w=e.other();
adj[v].add(e);
adj[w].add(e);
}
public Iterable<Edge> adj(int v){
return adj[v];
}
}
-
Prim 算法
实现思路:优先队列 每次都加入最小的边
实现方式:懒实现和热实现
//visit操作 将该节点有关的所有边都加入到优先队列中,并将该节点标记为已添加到最小生成树中 private void visit(WeightedGraph G,int v) { marked[v]=true; for(Edge e:G.adj(v)){ if(!marked[e.other(v)]) pq.insert(e); } } //lazy implementation(往优先队列里添加该点连接的所有边,判断该点连接的所有边所对应的点是否已经在最小生成树中,如果在则该边设为无效边,在生成树时,优先队列会删除在最顶部的无效边) public class LazyPrimMST{ private boolean[] marked;//判断该点是否已经添加到最小生成树中 private Queue<Edge> mst;//最小生成树 private MinPQ<Edge> pq;//优先队列将边拍好序 public LazyPrimMST(WeightedGraph G){ pq=new MinPQ<Edge>(); mst=new Queue<Edge>(); marked=new boolean[G.V()]; visit(G,0);//将0号点添加到最小生成树中去 while(!pq.isEmpty()&&mst.size()<G.V()-1) { Edge=pq.delMin(); int v=e.either(),w=e.other(); if(marked[v]&&markde[w]) continue; mst.enqueue(w); if(!marked[v]) visit(G,v); if(!marked[w]) visit(G,w); } } } //eager implementation(每次都选择最小的边,新节点到达其他节点的边长若是小于原来队列里到达该节点的距离则修改到达该节点的距离,大于的话不修改,如果没有该节点的话则添加该节点,已经到达过的节点则不用添加到优先队列里面)
懒实现举例图
热实现举例图
-
Kruskal
//按照weight排好序依次连接看是否有环生成,有环则不加入到最小生成树,直到所有节点都连接上 public class KruskalMST{ private Queue<Edge> mst=new Queue<Edge>(); //生成最小搜索树 public KruskalMST(EdgeWeightGraph 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(); if(!uf.connect(v,w))//判断是否有环 { uf.union(v,w);//无环则添加进去 mst.enqueue(e); } } } public Iterable<Edge> edges() { return mst; } }
最短路径算法
给定一个节点,找到它到其他节点的最短距离
//有向边
public class DirectedEdge{
int v,w;
double weight;
public DirectedEdge(int v,int w,double weight)
{
this.v=v;
this.w=w;
this.weight=weight;
}
public int from(){
return v;
}
public int to(){
return w;
}
public double weight(){
return weight;
}
}
//有向边构成的图
public class EdgeWeightedDigraph{
private final int V;
private final Bag<DirectedEdge>[] adj;
public EdgeWeightedDigraph(int V){
this.V=V;
adj=(Bag<DirectedEdge>[]) new Bag[V];
}
public void addEdge(DirectedEdge e){
int v=e.from();
adj[v].add(e);
}
public Iterable<DirectedEdge> adj(int v)
{
return adj[v];
}
}
//概念说明
//distTo[v] 是已知的从s到v的最短路径
//distTo[w] 是已知的从s到w的最短路径
//edgeTo[w] 是已知的从s到w的最短路径的最后一条边
//如果从e=v->w比原来distTo[w]小则更新distTo[w] and edgeto[w]
//更新操作
private void relax(DirectedEdge e)
{
int v=e.from(),w=e.to();
if(distTo[w]>distTo[v]+e.weight())
{
distTo[w]=distTo[v]+e.weight();
edgeTo[w]=e;
}
}
//狄杰斯特拉算法
//初始化到所有节点的距离为无穷,用一个优先队列,去不断更新能到达的节u点中的距离,每次选择最短的距离添加到结果中。
public class DijkstraSP
{
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPQ<Double> pq;
public DijkstraSP(EdgeWeightedDigraph G,ins s)
{
edgeTo=new DirectedEdge[G.V()];
distTo=new Double[G.V()];
pq=new IndexMinPQ<Double>(G.V());
//初始化 源节点距离为零,其他为无穷大
for(int v=0;v<G.V();v++)
distTo[v]=Double.POSITIVE_INFINITY;
distTo[0]=0;
pq.insert(s,0.0);
while(!pq.isEmpty()){
int v=pq.delMin();
for(Directed e:G.adj(v))
relax(e);//不断更新最短距离
}
}
}
//relax操作相比上述写的补充
private void relax(DirectedEdge e){
int v=e.from(),w=e.to();
if(distTo[w]>distTo[v]+e.weight()){
distTo[w]=distTo[v]+e.weight();
edgeTo[w]=e;
if(pq.contains(w)) pq.decreaseKey(w,distTo[w]);
else pq.insert(w,distTo[w]);
}
}
//bellman ford算法
//每次迭代找到一个最小的,(m次迭代找到不超过m条的边)
//判断图中是否有负权回路(如果有负权回路则一直迭代到负无穷就没有最短路)[每次迭代前面已经找到的边不会再更新,若更新则说明里面有负权回路]
//时间复杂度为O(m*n)
狄杰斯特拉算法结果图