与最小生成树学习相比,一开始我有点昏了。感觉既然都能计算出 “最短” 路径,为什么还要专门的研究最短路径的Dijkstra,Floyd呢?
后来才知道,最小生成树是保证这个图的所有路径加起来最短!
而最短路径保证A点到B的路径最短,并不能保证这个图的路径最短!
Dijkstra:
狄克斯特拉算法,这个算法,我到现在都感觉跟Prim算法有一些相同之处。
他们都是一开始按照起始点,保存起始点所能达到的点的距离。
然后从中找出一个最近的点k,然后再根据点k所能到达的A点的距离更新原先起始点所能到达的A点的距离,前提起始点到K点再到A点的距离小于起始点直接到达A点的距离
我想着两个算法有两个区别 :1、Prim是只要已经连通的点中,假设k点,k点到A点的距离小于原先已更新的到达A点的最短距离,那就更新lowcost
Kruscal则是,K点到A点的距离小于起始点到达A点的距离,则更新
2、Prim对于已经确定加入S集合的点,根据之后所能到达的点跟已经加入S点的距离相比,如果比之前的最短距离还小,还会重新将其从S集合中拿出(即重新规划路线)
Kruscal则是已经确定加入S集合的点,就不会再将其从S集合中拿出来了
原因是:Prim查找的最近距离K点,是已经加入S集合中某个点距离K点距离最短即可
Kruscal则是距离起始点最近的点,才能作为K点,所以之后找到的肯定都是大于之前的距离的,亦或是原先起始点到达不了的点,想要到达这个点必须经过之前的点,所以距离怎么都不会比之前的短。
public class Dijkstra {
private final int INF = 32767;
void Shorted_Path(MGraph g,int v){
int s[] = new int[g.n];//保存点是否已经加入集合S,0未加入,1加入
int dist[] = new int[g.n];//保存v点到其他点的距离
int path[] = new int[g.n];//保存v点到达该点的前一个点编号
for(int i = 0 ; i < g.n ; i ++){
s[i] = 0;//表示没有放入S集合中
dist[i] = g.edges[v][i];
path[i] = v;
}
s[v] = 1;//v已经放入集合S中
path[v] = 0;//且上一个点不存在
for(int i = 1 ; i < g.n ; i ++){//查找到剩下n-1个点的距离
int min = 32767;
int k = 0;
for(int j = 0 ; j < g.n ; j ++){
if (s[j] == 0 && dist[j] < min) {
min = dist[j];
k = j;
}
}
s[k] = 1;
for(int j = 0 ; j < g.n ; j ++){//更新dist和path,修改不在s中的顶点的距离
if (s[j] == 0) {
if (g.edges[k][j] < INF && g.edges[k][j] + dist[k] < dist[j]) {
dist[j] = g.edges[k][j] + dist[k];
path[j] = k;
}
}
}
}
Dispath(s,dist,path,v,g.n);//输出路径
}
private void Dispath(int[] s,int[] dist,int[] path,int v,int n){
for(int i = 0 ; i < n ; i ++){
if (s[i] == 1) {
System.out.println("起始点:"+v);
System.out.println("终止点:"+i);
System.out.println("最短路径:"+dist[i]);
Ppath(path, i, v);
}
}
}
private void Ppath(int[] path, int i,int v){
int k = path[i];
if (k == v) return;
Ppath(path, k, v);
System.out.println(k+",");
}
}
Floyd:
弗洛伊德算法:这个算法第一次接触还是大一参加ACM的时候,那个时候蛮喜欢的,因为简单易记!三重循环就好了!
原理:对于每两个点,都遍历所有的点作为中间点搭桥,判断出最短的距离,其他的点亦可以在这个这两个点的基础上判断出最短距离
public class Floyd {
private final int INF = 32768;
private final int MAX = 100;
public void Shortest_Path(MGraph g){
int A[][] = new int[g.n][g.n];
int path[][] = new int[g.n][g.n];
for(int i = 0 ; i < g.n ; i ++){
for(int j = 0 ; j < g.n ; j ++){
A[i][j] = g.edges[i][j];
path[i][j] = -1;//表示没有经过任何中间点;
}
}
for(int k = 0 ; k < g.n ; k ++){
for(int i = 0 ; i < g.n ; i ++){
for(int j = 0 ; j < g.n ; j ++){
if (A[i][j] < A[i][k] + A[k][j]) {
A[i][j] = A[i][k] + A[k][j];
path[i][j] = k;
}
}
}
}
Dispath(A,path,g.n);
}
private void Dispath(int[][] a, int[][] path, int n) {
for(int i = 0 ; i < n ; i ++){
for(int j = 0 ; j < n ; j ++){
if (a[i][j] == INF) {
if (i != j) {
System.out.println("从"+i+"到"+j+"没有路径");
}
}else{
System.out.println("从"+i+"到"+j+"的距离为:"+a[i][j]);
System.out.println("路径为:"+i);
Ppath(path,i,j);
System.out.println(","+j);
}
}
}
}
private void Ppath(int[][] path, int i, int j) {
if (i == j) return;
int k = path[i][j];
Ppath(path, i, k);
System.out.println(","+k);
Ppath(path, k, j);
}
}