您的位置:首页 > 编程语言 > Java开发

【java】最短路径算法

2017-05-09 18:41 369 查看

Dijkstra算法

Dijkstra算法可用于求正权图上的单源最短路径,该算法适用于有向图和无向图。如果要求每两点之间的最短路径,需要调用n次Dijkstra算法,或者使用Floyd算法。

该算法的伪代码:

清除所有点的标号
设d[0]=0,其他d[i]=INF
循环n次
{
在所有未标号节点中,选出d值最小的节点x
给结点x标记
对于从x出发的所有边(x,y),更新d[y]=min{d[y],d[x]+w[x,y]}
}

更新d[y]的过程又称为松弛操作,即对迄今为止找到的v的最短路径进行改进。

先输入结点数目n,边的数目m,之后输入m行数据,每行代表一条边的起点,终点,权值,最后输入开始结点的编号,最后输出从开始结点到其余各点的最短路径。



样例输入:

7 11

0 1 7

0 3 5

1 2 8

1 3 9

1 4 7

2 4 5

3 4 15

3 5 6

4 5 8

4 6 9

5 6 11

0

样例输出:

0 7 15 5 14 11 22

import java.util.Arrays;
import java.util.Scanner;

public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
int n=scanner.nextInt();
int m=scanner.nextInt();
int[][] edges=new int

;
for(int i=0;i<m;i++){
int u=scanner.nextInt();
int v=scanner.nextInt();
int weight=scanner.nextInt();
edges[u][v]=weight;
edges[v][u]=weight;
}
int src=scanner.nextInt();
int[] dist=new int
;			//存放源点到各点的最短路径长度,d[src]=0
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src]=0;
int[] visited=new int
;
for(int i=0;i<n;i++){			//循环n次,每次可以得到源点src到一个点的最短路径
int x=-1,min=Integer.MAX_VALUE;
for(int j=0;j<n;j++){		//选出未访问的结点中dist最小的结点
if(visited[j]==0&&dist[j]<min){
x=j;
min=dist[j];
}
}
visited[x]=1;
for(int k=0;k<n;k++){		//更新dist值,松弛操作
if(edges[x][k]>0){
int temp=dist[x]+edges[x][k];
if(dist[k]>temp)
dist[k]=temp;
}
}
}
StringBuffer str=new StringBuffer();
for(int i:dist)
str.append(i+" ");
System.out.print(str.substring(0,str.length()-1));
}
scanner.close();
}
}


对于以上算法的优化,可分两个部分:

①更新dist[]值,以上算法使用邻接矩阵存储图,这样,每次更新dist都需要循环n次,当我们改用邻接表存储边时,可减少循环次数。

②查找dist[]中最小的值,以上算法每次都需要循环n次,我们可以使用优先队列实现这一过程,在优先队列中,元素并不是按照进入队列的先后顺序排列,而是按照优先级的高低顺序排列。Java的优先队列每次取最小元素,C++的优先队列每次取最大元素。

import java.util.Arrays;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Scanner;

class MyComparator implements Comparator<Pair>{
public int compare(Pair p1, Pair p2) {
if(p1.dist>p2.dist)
return 1;
else if(p1.dist<p2.dist)
return -1;
return 0;
}
}
class Pair{
int index;
int dist;
Pair(int index,int dist){
this.index=index;
this.dist=dist;
}
}
public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
int n=scanner.nextInt();
int m=scanner.nextInt();
int[][] edges=new int

;
for(int i=0;i<m;i++){
int u=scanner.nextInt();
int v=scanner.nextInt();
int weight=scanner.nextInt();
edges[u][v]=weight;
edges[v][u]=weight;
}
int src=scanner.nextInt();
int[] dist=new int
;			//存放源结点到其余结点的最短路径长度,dist[src]=0,其余为INF
Arrays.fill(dist, Integer.MAX_VALUE);
dist[src]=0;
PriorityQueue<Pair> queue=new PriorityQueue<>(new MyComparator());
queue.offer(new Pair(src,0));
int[] fa=new int
;			//存放最短路径中到该节点的上一个节点,fa[src]=src,如不需要输出路径可省略
fa[src]=src;
Arrays.fill(fa, -1);
int[] visited=new int
;
while(!queue.isEmpty()){
Pair pair=queue.poll();
int index=pair.index;
visited[index]=1;
for(int i=0;i<n;i++){
if(visited[i]==0&&edges[index][i]>0){
int temp=pair.dist+edges[index][i];
if(dist[i]>temp){
dist[i]=temp;
fa[i]=index;
queue.offer(new Pair(i,dist[i]));//减少了冗余的松弛操作
}
}
}
}
StringBuffer str=new StringBuffer();
for(int i:dist)
str.append(i+" ");
System.out.print(str.substring(0,str.length()-1));
}
scanner.close();
}
}
以上算法中,优先队列PriorityQueue的存储情况:

①(0,0)

②(1,7) (3,5)

③(1,7) (4,20) (5,11)

④(4,20) (5,11) (2,15) (4,14)

省略......

可以看出一个结点会进入队列多次,而优先队列只是选出最小的那个而已。在java中,优先队列Prior
4000
ityQueue是用小顶堆实现的。

Floyd算法

 Floyd算法是一种利用动态规划的思想寻找给定的加权图中多源点之间最短路径的算法。
分析:从结点i到结点j的最短距离,要么是从i直接到j,要么是从结点i,经过某些中转结点k,最后到结点j。
设dist(i,j)表示结点i到结点j的最短距离,则其状态转移方程是:dist[i,j]:=min{dist[i,k]+dist[k,j],dist[i,j]}。

先输入结点数目n,边的数目m,之后输入m行数据,每行代表一条边的起点,终点,权值,最后输出所有结点到其余各点的最短路径长度。

样例输入:
7 11

0 1 7

0 3 5

1 2 8

1 3 9

1 4 7

2 4 5

3 4 15

3 5 6

4 5 8

4 6 9

5 6 11

样例输出:
0 7 15 5 14 11 22

7 0 8 9 7 15 16

15 8 0 17 5 13 14

5 9 17 0 14 6 17

14 7 5 14 0 8 9

11 15 13 6 8 0 11

22 16 14 17 9 11 0

import java.util.Scanner;

public class Main {
public static void main(String[] args){
Scanner scanner = new Scanner(System.in);
while(scanner.hasNext()){
int n=scanner.nextInt();
int m=scanner.nextInt();
int[][] edges=new int

;
for(int i=0;i<m;i++){
int u=scanner.nextInt();
int v=scanner.nextInt();
int weight=scanner.nextInt();
edges[u][v]=weight;
edges[v][u]=weight;
}
int[][] dist=new int

;			//dist[i][j]存放结点i到结点j的最短路径大小
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(i==j)
dist[i][j]=0;
else
dist[i][j]=edges[i][j]==0?Integer.MAX_VALUE:edges[i][j];
}
for(int k=0;k<n;k++)
for(int i=0;i<n;i++)
for(int j=0;j<n;j++){
if(dist[i][k]<Integer.MAX_VALUE&&dist[k][j]<Integer.MAX_VALUE
&&dist[i][j]>dist[i][k]+dist[k][j])
dist[i][j]=dist[i][k]+dist[k][j];
}
for(int i=0;i<n;i++){
StringBuffer str=new StringBuffer();
for(int j=0;j<n;j++)
str.append(dist[i][j]+" ");
System.out.println(str.substring(0,str.length()-1));
}
}
scanner.close();
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: