Floyd算法讲解(多源最短路径问题求解)
五行代码实现的算法好用吗?
答案当时是肯定的
今天就来分析一波图论问题中最最经典的最短路径算法
适用条件:
在有向图中,求一个顶点到其他顶点的最短路径
首先让我们先回顾一下,最简单的多源最短路径的求法:Floyd算法
实例讲解
这里呢我们通过这个例题对这个算法进行解析:(节选自《啊哈算法》)
题目中的关系如图所示:
这里呢,我们依旧使用二维数组来存储顶点之间边的关系
为了让最后单源路径最短的问题方便读取答案,创建一个1号顶点到其他顶点初始路程的数组dist
求解思路
1、首先从距离起点最近的点进行操作,因为起点1不可能通过其他顶点获得到达该最近点的最短路径。
2、那么我们找到了顶点2,和Floyd方法类似,这个时候我们开始经过顶点2对顶点1的其他边进行优化。
3、比较dist[3]与dist[2]+e[2][3]的大小关系,如果dist[3]较大,则进行更新,这就使得顶点1到3的最短路径变短。
4、接下来,再剩下的3,4,5和6号顶点中,继续寻找离1号起点最近的顶点,进行路径更新。
5、最终通过其他顶点的优化后的道德单源最短路径为:
总结算法:
1、通过book[i]是否为1判断点i是否已经是最短路径的顶点集合P中,否则在Q中。
2、在Q中选取一个离起点最近的顶点加入到集合P中,并考察所有的以点u为起点的边
3、重复第二步,直到Q为空
算法实现:
/*
*C语言实现,最简
*/
#include <stdio.h>
int main()
{
int e[10][10],dis[10],book[10],i,j,n,m,t1,t2,t3,u,v,min;
int inf = 99999999;//用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
scanf("%d %d",&n,&m);
//初始化
for(i = 1;i <= n;i++) {
for(j = 1;j <= n;j++){
if(i == j) e[i][j] = 0;
else e[i][j] = inf;
}
}
//读入边
for (int i = 0; i < m; i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2] = t3;
}
//初始化dis数组
for(i = 1;i <= n;i++) {
dis[i] = e[1][i];
}
//book数组初始化
for(i = 1;i <= n;i++)
book[i] = 0;
book[1] = 1;
//Dijkstra算法核心语句
for(i = 1;i <= n - 1;i++)
{
//找到离1号顶点最近的顶点
min = inf;
for(j = 1;j <= n;i++)
{
if(book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
book[u] = 1;
for(v = 1;v <= n;v++)
{
if(e[u][v] < inf){
if(dis[v] > dis[u] + e[u][v])
dis[v] = dis[u] + e[u][v];
}
}
}
//输出最终结果
for(i = 1;i <= n;i++)
prntf("%d ",dis[i]);
getchar();
getchar();
return 0;
}
/*
*Java实现,非最优化解
*/
package com.stortest.path;
import java.util.ArrayList;
import java.util.Scanner;
public class Dijkstra {
public long[] result;//定义全局变量存储到各点的最短路径
public class Edge{
public int a,b,c;
Edge(int a,int b,int c){//声明构造函数
this.a = a;
this.b = b;
this.c = c;
}
}
public boolean get(int n, int s, Edge[] A) {
ArrayList<Integer> list = new ArrayList<Integer>();//生成list列表
//初始化
result = new long[n];
boolean[] used = new boolean[n];//存储访问状态
int[] num = new int[n];
for(int i = 0;i < n;i++) {
result[i] = Integer.MAX_VALUE;
used[i] = false;
}
result[s] = 0; //第s个顶点到自身距离为0
used[s] = true; //表示第s个顶点进入数组队
list.add(s); //第s个顶点入队
while(list.size() != 0) {
int a = list.get(0); //获取数组队中第一个元素
list.remove(0); //删除数组队中第一个元素
for(int i = 0;i < A.length;i++) {
//当list数组队的第一个元素等于边A[i]的起点时
if(a == A[i].a && result[A[i].b] > result[A[i].a] + A[i].c) {
//判断 起点到终点的最短路径 是否可以更新
result[A[i].b] = result[A[i].a] + A[i].c;
if(!used[A[i].b]) {//如果终点是第一次被遍历
list.add(A[i].b);
used[A[i].b] = true; //表示边A[i]的终点b已进入数组队
}
}
}
// used[a] = false; //顶点a出数组对
}
return true;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Dijkstra text = new Dijkstra();//创建对象
Scanner input = new Scanner(System.in);
int n = input.nextInt();
int m = input.nextInt();
Edge[] A = new Edge[m];//创建内部类对象数组,存储边的关系
for(int i = 0;i < m;i++)
{
int a = input.nextInt() - 1;
int b = input.nextInt() - 1;
int c = input.nextInt();
A[i] = text.new Edge(a,b,c);
}//接收输入数据
if(text.get(n,0,A)){//调用子函数,输出到最远点的结果
System.out.println(text.result[text.result.length - 1]);
}
}
}
改进:
该算法的时间复杂度为O(N2),对于边数M少于N2的稀疏图来说,可以使用邻接矩阵进行优化,可以使时间复杂度优化到(M+N)logN