最短路径算法
两种算法的本质其实都是一种遍历,和一般的遍历不同的是,他们巧妙的保存了中间的 计算结果,因此减少了一些中间重复的计算过程
目录
1 Dijkstra算法
原理:
- 其实质是一种遍历,所以时间复杂度是O(n^2)
- 跟普通的遍历算法不同的是,它能用最小的空间,保存一个点到其他所有点的最短距离,但是无法记录路径。
- Dijkstra算法就像是一个波的扩散过程,每一个波的周边继续不断的扩散,然后覆盖整个区域,扩散时,每次都选择最短的边,进行扩散,以免乱。
- 算法步骤
1 初始化visit数组,和边数组
2 选择连接已访问的点S和未访问的V中最短的一条边 ,端点为u
3 将u加到S中,重新更新邻接矩阵
4.如果V中还有元素,重复2 3
5.输出邻接矩阵的第一行
源码:
Java实现
import java.util.Scanner;
public class Dijisktra {
public static void main(String[] args) {
int[][] e = new int[10][10]; //这里简单的设置为了 静态
int[] dis = new int[10];
int[] book = new int[10];
int i, j, n, m, t1, t2, t3, u = 0, v, min;
int inf = 99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
Scanner sc = new Scanner(System.in);
// System.out.println("请输入顶点数和边数 如6 9 ");
// n = sc.nextInt();
// m = sc.nextInt();
n=6;
m=9;
//初始化
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (i == j) e[i][j] = 0;
else e[i][j] = inf;
// //读入边
// System.out.println("请输入边的参数 如1 2 1");
// for (i = 1; i <= m; i++) {
// t1 = sc.nextInt();
// t2 = sc.nextInt();
// t3 = sc.nextInt();
// e[t1][t2] = t3;
// }
e[1][2] = 1;
e[1][3] = 12;
e[2][3] = 9;
e[2][4] = 3;
e[3][5] = 5;
e[4][3] = 4;
e[4][5] = 13;
e[4][6] = 15;
e[5][6] = 4;
//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
//其实就是邻接矩阵的第一行
for (i = 1; i <= n; i++)
dis[i] = e[1][i];
//book数组初始化
//book[i]=1表示该节点已经在S中 否则就是在V中
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; j++) {
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++)
System.out.printf("%4d",dis[i]);
}
}
C++/c实现
http://wiki.jikexueyuan.com/project/easy-learn-algorithm/dijkstra.html
#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(i=1;i<=m;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
//初始化dis数组,这里是1号顶点到其余各个顶点的初始路程
//其实就是邻接矩阵的第一行
for(i=1;i<=n;i++)
dis[i]=e[1][i];
//book数组初始化
//book[i]=1表示该节点已经在S中 否则就是在V中
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;j++)
{
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++)
printf("%d ",dis[i]);
getchar();
getchar();
return 0;
}
2 Floyd算法
原理:
- 对于每一对顶点 u 和 v,看看是否存在一个顶点 w 使得从 u 到 w 再到 v 比己知的路径更短。如果是更新它。 时间复杂度O(N3)
- 如要从0-7,先看看加入中间节点1会不会短一点,如果短一点就0-1-7,然后再看再加入一个2会不会更短一点,如果更短就0-1-2-7,以此类推直到所有的点都遍历一遍,值得注意的是如果2-7之间不是直接相连,可能暂时先不更新,而是2-5、5-7先连接,然后当扩散到以5为中心点的时候,再连接上了,再更新。floyd的算法就像是破圈法的逆方法一样,先局部的形成一些小联通区域,最后慢慢的扩大到整个范围。
- 算法步骤
1初始化 邻接矩阵
2.选择一个点作为中心点,并计算从不同的起点出发到达不同的目标点的距离 更新邻接矩阵
3.如果中心点,没有遍历完,重复2
4.输出邻接矩阵
源代码:
//Floyd-Warshall算法核心语句
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][k]<inf && e[k][j]<inf && e[i][j]>e[i][k]+e[k][j])
e[i][j]=e[i][k]+e[k][j];
实现过程
Java实现
public class Floyd {
public static void main(String[] args) {
{
int[][] e = new int[10][10];
int k, i, j, n, m, t1, t2, t3;
int inf = 99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
// Scanner sc = new Scanner(System.in);
// System.out.println("请输入顶点数和边数 如6 9 ");
// n = sc.nextInt();
// m = sc.nextInt();
n = 4;
m = 8;
//初始化
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (i == j) e[i][j] = 0;
else e[i][j] = inf;
//读入边
// System.out.println("请输入边的参数 如1 2 1");
// for (i = 1; i <= m; i++) {
// t1 = sc.nextInt();
// t2 = sc.nextInt();
// t3 = sc.nextInt();
// e[t1][t2] = t3;
// }
e[1][2] = 2;
e[1][3] = 6;
e[1][4] = 4;
e[2][3] = 3;
e[3][1] = 7;
e[3][4] = 1;
e[4][1] = 5;
e[4][3] = 12;
//Floyd-Warshall算法核心语句
//k是中间节点
//i是源jiedian
//j是目标节点
for (k = 1; k <= n; k++)
for (i = 1; i <= n; i++)
for (j = 1; j <= n; j++)
if (e[i][j] > e[i][k] + e[k][j])
e[i][j] = e[i][k] + e[k][j];
//输出最终的结果
//输出的是一个方阵 记录的是所有点到所有点的最短
for (i = 1; i <= n; i++) {
for (j = 1; j <= n; j++) {
System.out.printf("%10d", e[i][j]);
}
System.out.println(' ');
}
}
}
}
C++/C实现
http://wiki.jikexueyuan.com/project/easy-learn-algorithm/floyd.html
//注意本文给出的例子是一个有向图
#include <stdio.h>
int main()
{
int e[10][10],k,i,j,n,m,t1,t2,t3;
int inf=99999999; //用inf(infinity的缩写)存储一个我们认为的正无穷值
//读入n和m,n表示顶点个数,m表示边的条数
// scanf("%d %d",&n,&m);
n=4;
m=8;
//初始化
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(i=1;i<=m;i++)
{
scanf("%d %d %d",&t1,&t2,&t3);
e[t1][t2]=t3;
}
*/
e[1][2]=2;
e[1][3]=6;
e[1][4]=4;
e[2][3]=3;
e[3][1]=7;
e[3][4]=1;
e[4][1]=5;
e[4][3]=12;
//Floyd-Warshall算法核心语句
//k是中间节点
//i是源jiedian
//j是目标节点
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
if(e[i][j]>e[i][k]+e[k][j] )
e[i][j]=e[i][k]+e[k][j];
//输出最终的结果
//输出的是一个方阵 记录的是所有点到所有点的最短
for(i=1;i<=n;i++)
{
for(j=1;j<=n;j++)
{
printf("%10d",e[i][j]);
}
printf("\n");
}
return 0;
}