19、加权有向图
19.1、边的表示
package chapter19;
public class DirectedEdge {
private final int v;
private final int w;
private final 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 getWeight(){
return weight;
}
}
19.2、加权有向图的实现
package chapter19;
import chapter03.Queue;
import chapter17.Edge;
public class EdgeWeightedDigraph {
private final int vNum;
private int eNum;
private Queue<DirectedEdge>[] adj;
public EdgeWeightedDigraph(int vNum) {
this.vNum = vNum;
this.eNum = 0;
this.adj = new Queue[vNum];
for (int i = 0; i < vNum; i++) {
this.adj[i] = new Queue<DirectedEdge>();
}
}
public int getVNum() {
return vNum;
}
public int getENum() {
return eNum;
}
public void addEdge(DirectedEdge e) {
int v = e.from();
this.adj[v].enQueue(e);
eNum++;
}
public Queue<DirectedEdge> adj(int v) {
return this.adj[v];
}
public Queue<DirectedEdge> edges() {
Queue<DirectedEdge> allEdges = new Queue<>();
for (int v = 0; v < vNum; v++) {
for (DirectedEdge e : adj(v)) {
allEdges.enQueue(e);
}
}
return allEdges;
}
}
20、最短路径
20.1、定义及性质
-
定义
在一副加权有向图中,所有从顶点 s 到顶点 t 的路径中 总权重最小 的那条路径
-
性质
-
路径具有方向性 -
权重不一定等价于距离
权重可以是距离、时间、花费等内容,权重最小指的是成本最低
-
只考虑连通图
一副图中并不是所有的顶点都是可达的,如果s和t不可达,那么它们之间也就不存在最短路径,为了简化问题,这里只考虑连通图
-
最短路径不一定是唯一的
从一个顶点到达另外一个顶点的权重最小的路径可能会有很多条,这里只需要找出一条即可
-
最短路径树
- 给定一副加权有向图和一个顶点s,以s为起点的一棵最短路径树是图的一副子图,它包含顶点s以及从s可达的所有顶点。这棵有向树的根结点为s,树的每条路径都是有向图中的一条最短路径
20.2、最短路径树API设计
20.3、松弛技术
松弛这个词来源于生活:一条橡皮筋沿着两个顶点的某条路径紧紧展开,如果这两个顶点之间的路径不止一条,还有存在更短的路径,那么把皮筋转移到更短的路径上,皮筋就可以放松了
松弛这种简单的原理刚好可以用来计算最短路径树
在API中,需要用到两个成员变量edgeTo和distTo,分别存储边和权重。一开始给定一幅图G和顶点s,只知道图的边以及这些边的权重,其他的一无所知,此时初始化顶点s到顶点s的最短路径的总权重disto[s]=0;顶点s到其他顶点的总权重默认为无穷大,随着算法的执行,不断的使用松弛技术处理图的边和顶点,并按一定的条件更新edgeTo和distTo中的数据,最终就可以得到最短路径树
-
边的松弛
放松边 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这条边
-
顶点的松弛
顶点的松弛是基于边的松弛完成的,只需要把某个顶点指出的所有边松弛,那么该顶点就松弛完毕
例如:
要松弛顶点v,只需要遍历v的邻接表,把每一条边都松弛,那么顶点v就松弛了。 如果把起点设置为顶点0,那么找出起点0到顶点6的最短路径0->2->7>3->6的过程如下:
20.4、Dijkstra算法实现
package chapter20;
import chapter03.Queue;
import chapter07.IndexMinPriorityQueue;
import chapter19.DirectedEdge;
import chapter19.EdgeWeightedDigraph;
public class DijkstraSP {
private DirectedEdge[] edgeTo;
private double[] distTo;
private IndexMinPriorityQueue<Double> pq;
public DijkstraSP(EdgeWeightedDigraph g,int s) {
this.edgeTo = new DirectedEdge[g.getVNum()];
this.distTo = new double[g.getVNum()];
for(int i = 0;i<g.getVNum();i++){
this.distTo[i] = Double.POSITIVE_INFINITY;
}
this.pq = new IndexMinPriorityQueue<>(g.getVNum());
this.distTo[s] = 0.0;
this.pq.insert(s,0.0);
while(!this.pq.isEmpty()){
relax(g,this.pq.delMin());
}
}
private void relax(EdgeWeightedDigraph g,int v){
for (DirectedEdge edge : g.adj(v)) {
int w = edge.to();
if(distTo[v] + edge.getWeight() < distTo[w]){
distTo[w] = distTo[v] + edge.getWeight();
edgeTo[w] = edge;
if(pq.contains(w)){
pq.changeItem(w, edge.getWeight());
}else{
pq.insert(w, edge.getWeight());
}
}
}
}
public double distTo(int v){
return distTo[v];
}
public boolean hasPathTo(int v){
return distTo[v] < Double.POSITIVE_INFINITY;
}
public Queue<DirectedEdge> pathTo(int v){
if(!hasPathTo(v)){
return null;
}
Queue<DirectedEdge> allEdges = new Queue<>();
while (true){
DirectedEdge e = edgeTo[v];
if(e == null){
break;
}
allEdges.enQueue(e);
v = e.from();
}
return allEdges;
}
}
package chapter20;
import chapter03.Queue;
import chapter19.DirectedEdge;
import chapter19.EdgeWeightedDigraph;
import org.junit.Test;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class DijkstraSPTest {
@Test
public void test() throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(DijkstraSPTest.class.getClassLoader().getResourceAsStream("min_route_test.txt")));
int total = Integer.parseInt(br.readLine());
EdgeWeightedDigraph g = new EdgeWeightedDigraph(total);
int edgeNum = Integer.parseInt(br.readLine());
for (int i = 0; i < edgeNum; i++) {
String[] s = br.readLine().split(" ");
int v = Integer.parseInt(s[0]);
int w = Integer.parseInt(s[1]);
double weight = Double.parseDouble(s[2]);
DirectedEdge edge = new DirectedEdge(v, w, weight);
g.addEdge(edge);
}
DijkstraSP sp = new DijkstraSP(g, 0);
Queue<DirectedEdge> edges = sp.pathTo(6);
for (DirectedEdge e : edges) {
int v = e.from();
int w = e.to();
double weight = e.getWeight();
System.out.println(v + " - " + w + " : " + weight);
}
}
}
8
15
4 5 0.35
5 4 0.35
4 7 0.37
5 7 0.28
7 5 0.28
5 1 0.32
0 4 0.38
0 2 0.26
7 3 0.39
1 3 0.29
2 7 0.34
6 2 0.40
3 6 0.52
6 0 0.58
6 4 0.93
3 - 6 : 0.52
7 - 3 : 0.39
2 - 7 : 0.34
0 - 2 : 0.26
|