使用优先队列优化后的Dijkstra算法


一个简单的模板,需要注意的是当权值不存在或者权值都相同时,dijkstra算法变成bfs,而且dijkstra算法处理不了负权边情况。

一个重要的区别:(dijkstra与prim的区别)

在dij算法中dis[i]数组表达的意义是:节点i到源点start最短距离,松弛操作dis[i]>dis[t]+map[t][i](t节点是最新确定的最短路径节点,i节点既是t的一个邻接节点)的意义是:t的邻接节点i到源点start的最小距离(dis[i]),是否大于 最新节点t到原点的最短距离(dis[t])+节点t与节点i的权值(map[t][i]),这一过程都在围绕dis[i]数组进行。

在prim算法中dis[i]数组表达的意义是:节点i到  任意mst'节点' 的最小距离,dis[i]>x[t][i],(i为任意的未访问过的节点,t为最新的mst节点)意思是如果i节点到mst节点的距离要  大于  最新的mst节点t和i节点的权值,那么很显然为了满足总权值最小一定会有dis[i]=x[t][i]。

输出个点到原点的最短距离

第一行的数是源点,第二行的数是节点个数,第三行的数是边数

接下来的分别是初始节点,结束节点,和权值


测试用例:

1
5
7
1 2 2
1 3 3
2 3 5
2 4 6
3 4 7
3 5 1

4 5 4

答案:

0

2

3

8

4


import java.util.ArrayList;
import java.util.Comparator;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.Scanner;



public class 单源点最短路_Dijkstra算法 {

	/**
	 * 应用优先队列优化dijkastra解决单源点最短路径问题
	 * @param args
	 */
	static class node{
		public int nod;//节点标号
		public int sum;//当前nod节点到源点的最小的距离
		node(int nod,int sum){
			this.nod=nod;
			this.sum=sum;
		}
	}
	//邻接表节点
	static class edge{
		public int a;//起始节点
		public int b;//结束节点
		public int w;
		edge(int a,int b,int w){
			this.a=a;
			this.b=b;
			this.w=w;
		}
	}
	static final int INF=Integer.MAX_VALUE;
	static int vis[];//标记数组
	static int dis[];//dis[i]的意义是节点i到 源点 的最短路径,整个过程就是在维护这个数组
	public static void main(String[] args) {
		Scanner sc=new Scanner(System.in);
		int start=sc.nextInt();//起始点位置
		int n=sc.nextInt();//图的节点个数
		int m=sc.nextInt();//边数
//		sc.nextLine();
//		int map[][]=new int[n+1][n+1];//邻接矩阵
		//初始化邻接矩阵都为INF
//		for (int i = 0; i < map.length; i++) {
//			for (int j = 0; j < map[0].length; j++) {
//				map[i][j]=INF;
//			}
//		}
		//邻接表
		ArrayList<edge>[] map=new ArrayList[n+1];
		for (int i = 0; i < map.length; i++) {
			map[i]=new ArrayList<edge>();
		}
		//将数据录入邻接矩阵
		for (int i = 0; i < m; i++) {
			int a=sc.nextInt();
			int b=sc.nextInt();
			int w=sc.nextInt();
			map[a].add(new edge(a,b,w));
			//邻接矩阵方法
//			map[a][b]=Math.min(map[a][b],w);//可能会有两个节点间存在两条路径,总是选取最小的那条
		}
		//执行算法
		dijkstra(start,map,n,m);
		
		for (int i = 1; i < dis.length; i++) {
			System.out.println(dis[i]);
		}
	}
	/**
	 * 
	 * @param start 初始节点
	 * @param map 邻接矩阵
	 * @param n 节点数量
	 * @param m 边的数量
	 */
	private static void dijkstra(int start, ArrayList<edge>[] map, int n, int m) {
		//重写comparator方法,规定队列中小的数优先出队
		Comparator<node> com=new Comparator<node>(){
			@Override
			public int compare(node o1, node o2) {
				int num1=o1.sum;
				int num2=o2.sum;
				if(num1>num2){
					return 1;
				}else if (num1<num2) {
					return -1;
				}else {
					return 0;
				}
			}
		};
		//定义优先队列,初始值为11,比较器为com
		Queue<node> q=new PriorityQueue<node>(11,com);//初始化优先队列
		q.offer(new node(start,0));//初始节点入队
		vis=new int[n+1];//初始化 标记表
		dis=new int[n+1];//初始化 各节点到源点的最小距离表
		//都初始化为INF
		for (int i = 0; i <= n; i++) {
			dis[i]=INF;
		}
		//start节点到自身的距离为0
		dis[start]=0;
		while (q.size()!=0) {//当优先队列不为空时
			node temp=q.poll();//弹出并删除队列头结点
			int t=temp.nod;//提取头节点下表数
			if(vis[t]==1){//如果该节点已经被访问过了,直接跳过这次循环
				continue;
			}
			vis[t]=1;//没有访问过,标记访问过
			//遍历所有t的邻接节点
			for (int i = 0; i < map[t].size(); i++) {
				//如果该节点没有被访问过,且,tempnode到能够到达该节点(邻接)
				if(vis[map[t].get(i).b]==0 && map[t].get(i).w<INF){
					//松弛操作,更新dis表,如果这个邻接节点map[t].get(i).b到原地的距离要  大于  当前节点t到原点的距离+t节点到他的邻接点map[t].get(i).b 的距离之和
					if(dis[map[t].get(i).b]>dis[t]+map[t].get(i).w){
						dis[map[t].get(i).b]=dis[t]+map[t].get(i).w;//更新邻接点map[t].get(i).b到远点的距离
						q.offer(new node(map[t].get(i).b,dis[map[t].get(i).b]));//将更新的节点压入优先队列
					}
				}
			}
		}
	}
}


阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页