前几天讲了加权无向图的最小生成树,而今天讲的最短路径就是加权有向图的最小生成树。
定义:在一幅加权有向图中,从顶点s到顶点v的最短路径就是顶点s到顶点v所有路径中权重最小的那条路径。
如图所示:
性质:
①路径具有方向性
②权重不一定等价于距离,权重最小指的是成本最低
③只考虑连通图
④最短路径不一定是唯一的,只需要找出一条即可。
成员变量
private DirectedEdge[] edgeTo :索引代表顶点,值表示从顶点s到当前顶点的最短路径上的最后一条边
private double[] distTo : 索引代表顶点,值从顶点s到当前顶点的最短路径的总权重
private IndexMinPriorityQueue pq : 存放树中顶点与非树中顶点之间的有效横切边
边(v->w)的松弛
放松边v->w意味着检查从起点s到w的最短路径是否先从s到v,然后再从v到w?
如果是,则v->w这条边需要加入到最短路径树中,更新edgeTo和distTo中的内容:edgeTo[w]表示v->w这条边的DirectedEdge(加权边)对象,distTo[w]=distTo[v]+v->w这条边的权重; 如果不是,则忽略v->w这条边。如图所示:
顶点的松弛
只需要把顶点指出的所有边松弛即可。
构造方法
public DijkstraSP(EdgeWeightedDigraph G,int s){
//初始化成员变量
this.edgeTo=new DirectedEdge[G.getV()];
this.distTo=new double[G.getV()];
for (int i = 0; i < distTo.length; i++) {
distTo[i]=Double.POSITIVE_INFINITY;
}
this.pq=new IndexMinPriorityQueue<>(G.getV());
//找到G中以s为起点的最短路径树
//默认让顶点s进去到最短路径树
distTo[s]=0.0;
pq.insert(s,0.0);
//遍历pq
while (!pq.isEmpty()){
relax(G,pq.delMin());
}
}
松弛图G中的顶点v
private void relax(EdgeWeightedDigraph G,int v){
for (DirectedEdge e : G.adj(v)) {
//获取该变的终点w
int w = e.to();
//通过松弛技术,判断从起点s到w是否需要经过v到w这条边
if (distTo(v)+e.weight()<distTo(w)){
distTo[w]=distTo[v]+e.weight();
edgeTo[w]=e;
//判断pq是否已经存在w,如过存在,则更新权重,如果不在,则添加
if (pq.contains(w)){
pq.changeItem(w,distTo(w));
}else{
pq.insert(w,distTo(w));
}
}
}
}
查询从起点s到顶点v的最短路径中所有的边
public Queue<DirectedEdge> pathTo(int v){
//判断顶点s到v是否可达,如果不可达,返回null;
if (!haspathTo(v)){
return null;
}
//创建一个队列
Queue<DirectedEdge> alledges = new Queue<DirectedEdge>();
while (true){
DirectedEdge e = edgeTo[v];
if (e==null){
break;
}
alledges.enqueue(e);
v=e.from();
}
return alledges;
}
完整代码
public class DijkstraSP {
//索引代表顶点,值表示从顶点s到当前顶点的最短路径上的最后一条边
private DirectedEdge[] edgeTo;
//索引代表顶点,值从顶点s到当前顶点的最短路径的总权重
private double[] distTo;
//存放树中顶点与非树中顶点之间的有效横切边
private IndexMinPriorityQueue<Double> pq;
//根据一副加权有向图G和顶点s,创建一个计算顶点为s的最短路径树对象
public DijkstraSP(EdgeWeightedDigraph G,int s){
//初始化成员变量
this.edgeTo=new DirectedEdge[G.getV()];
this.distTo=new double[G.getV()];
for (int i = 0; i < distTo.length; i++) {
distTo[i]=Double.POSITIVE_INFINITY;
}
this.pq=new IndexMinPriorityQueue<>(G.getV());
//找到G中以s为起点的最短路径树
//默认让顶点s进去到最短路径树
distTo[s]=0.0;
pq.insert(s,0.0);
//遍历pq
while (!pq.isEmpty()){
relax(G,pq.delMin());
}
}
//松弛图G中的顶点v
private void relax(EdgeWeightedDigraph G,int v){
for (DirectedEdge e : G.adj(v)) {
//获取该变的终点w
int w = e.to();
//通过松弛技术,判断从起点s到w是否需要经过v到w这条边
if (distTo(v)+e.weight()<distTo(w)){
distTo[w]=distTo[v]+e.weight();
edgeTo[w]=e;
//判断pq是否已经存在w,如过存在,则更新权重,如果不在,则添加
if (pq.contains(w)){
pq.changeItem(w,distTo(w));
}else{
pq.insert(w,distTo(w));
}
}
}
}
//获取从顶点s到顶点v的最短路径的总权重
public double distTo(int v){
return distTo[v];
}
//判断从顶点s到顶点v是否可达
public boolean haspathTo(int v){
return distTo[v]<Double.POSITIVE_INFINITY;
}
//查询从起点s到顶点v的最短路径中所有的边
public Queue<DirectedEdge> pathTo(int v){
//判断顶点s到v是否可达,如果不可达,返回null;
if (!haspathTo(v)){
return null;
}
//创建一个队列
Queue<DirectedEdge> alledges = new Queue<DirectedEdge>();
while (true){
DirectedEdge e = edgeTo[v];
if (e==null){
break;
}
alledges.enqueue(e);
v=e.from();
}
return alledges;
}
}
b站详细讲解网址:http://yun.itheima.com/course/639.html