程序员必须会的基本算法8-Dijkstra算法(迪杰斯特拉算法)

Dijkstra算法

迪杰斯特拉(Dijkstra)算法是典型最短路径算法,
用于计算一个结点到其他结点的最短路径。
它的主要特点是以起始点为中心向外层层扩展(广度优先搜索思想),直到扩展到终点为止。

问题场景

在这里插入图片描述

有7个节点(A,B,C,D,E,F,G),
从G点出发,分别走到A,B,C,D,E,F六个节点
各个节点的距离用边线表示(权),比如A–B距离5
如何计算出G到其它各个节点的最短距离?
如果从其它点出发到各个点的最短距离又是多少?

代码解决

package basic;

public class Dijkstra
{
    
    
	/*
	 * 看完下面的,我们来总体看下迪杰斯特拉算法的运行策略
	 * 首先访问过的只有一个G,然后找到G能访问的节点中权重最小的
	 * 那就是GA,然后将GA看做一个节点,能访问到的节点中权重最小的
	 * 那就是GAB,然后又看作一个整体,下一个能访问到的节点中权重最小的
	 * 那就是GABE,就是这样的策略
	 */
	public static void main(String[] args)
	{
    
    
//		char[] nodes={ 'A', 'B', 'C', 'D', 'E', 'F', 'G' };
//		int[][] weight=new int[nodes.length][nodes.length];
//		final int N=Integer.MAX_VALUE/2;
//		
//		weight[0]=new int[]{N,5,7,N,N,N,2};  
//		weight[1]=new int[]{5,N,N,9,N,N,3};  
//		weight[2]=new int[]{7,N,N,N,8,N,N};  
//		weight[3]=new int[]{N,9,N,N,N,4,N};  
//		weight[4]=new int[]{N,N,8,N,N,5,4};  
//		weight[5]=new int[]{N,N,N,4,5,N,6};  
//		weight[6]=new int[]{2,3,N,N,4,6,N};
		//上面都是初始化数据
		char[] nodes={
    
     'A', 'B', 'C', 'D', 'E', 'F', 'G' ,'H','I'};
		int[][] weight=new int[nodes.length][nodes.length];
		final int N=Integer.MAX_VALUE/2;		
		
		weight[0]=new int[]{
    
    N,5,7,N,N,N,2,N,N};  
		weight[1]=new int[]{
    
    5,N,N,9,N,N,3,N,N};  
		weight[2]=new int[]{
    
    7,N,N,N,8,N,N,N,N};  
		weight[3]=new int[]{
    
    N,9,N,N,N,4,N,2,N};  
		weight[4]=new int[]{
    
    N,N,8,N,N,5,4,N,N};  
		weight[5]=new int[]{
    
    N,N,N,4,5,N,6,N,N};  
		weight[6]=new int[]{
    
    2,3,N,N,4,6,N,N,N};
		weight[7]=new int[]{
    
    N,N,N,2,N,N,N,N,2};
		weight[8]=new int[]{
    
    N,N,N,N,N,N,N,2,N};
		
		dijkstra(weight, 6);
	}
	public static void dijkstra(int[][] weight,int index)
	{
    
    
		//需要三个额外的变量来保存数据
		//这表示这个节点是否是已经被访问过了
		int[] vistied=new int[weight.length];
		//这表示路径是什么
		String[] paths=new String[weight.length];
		//这表示从出发点到当前节点的距离是多少
		int[] TheLength=new int[weight.length];
		
		//初始化路径
		for(int x=0;x<paths.length;x++)
		{
    
    
			paths[x]=new String(index+"->"+x);
		}
		//初始化出发点
		vistied[index]=1;
		TheLength[index]=0;
		/*
		 * 然后是遍历n-1个节点,因为第一个节点已经遍历过了
		 * 这里每一次都是找和出发点相通的,权重最小的那个节点,
		 * 然后对它进行深一层的遍历,看看它走到下一层后的最小路径是否能更加小
		 * 在这里你可能在想第二重的for循环会不会没有进去,就是那个找MinLength的for循环
		 * 然后让下标temp=-1,其实不会的,在后面的for遍历下一层的时候会改变下一层图的遍历
		 * 就是比如出发点是第零层,然后MinLength是找第二层节点的最小权重,然后下面的for会
		 * 根据第二层的最小权重去找第二层到第三层的最小权重,如果是出发点不能到达的节点
		 * 那么就肯定在第三层或者更加低的层,那么在第二层遍历会修改第三层节点的可达,就是修改权重
		 * 那么循环后再回到上面的寻找下一个最小权重的没有访问过的节点的时候就会可能不是从
		 * 第二层找起,而是直接到更加低的层次找
		 */
		for(int x=1;x<weight.length;x++)
		{
    
    
			int MinLength=Integer.MAX_VALUE/2;
			int temp=-1;
			for(int y=0;y<weight.length;y++)
			{
    
    
				if(vistied[y]==0 && weight[index][y]<MinLength)
				{
    
    
					MinLength=weight[index][y];
					temp=y;
				}
			}
			TheLength[temp]=MinLength;
			vistied[temp]=1;
			/**
			 * 首先是出发点到index节点的距离,然后index这个节点能访问的下一层节点
			 * 如果是从index这个节点出发访问到的下一层节点的距离加上从出发点到index的距离
			 * 比从出发点到index下一层节点的距离(这是不经过index节点的,从其他的路线出发的)小,
			 * 那么就将这TheLength的数组对应的index下一层节点对应的位置进行修改
			 */
			for(int y=0;y<weight.length;y++)
			{
    
    
				if(vistied[y]==0 && weight[index][temp]+weight[temp][y]<weight[index][y])
				{
    
    
					weight[index][y]=weight[index][temp]+weight[temp][y];
					paths[y]=paths[temp]+"->"+y;
				}
			}
		}
		for(int x=0;x<paths.length;x++)
		{
    
    
			if(x!=index)
			{
    
    
				System.out.println(index+"到"+x+"的路径:"+paths[x]+"		权重:"+TheLength[x]);
			}
		}
	}
}

结果

60的路径:6->0		权重:2
61的路径:6->1		权重:3
62的路径:6->0->2		权重:9
63的路径:6->5->3		权重:10
64的路径:6->4		权重:4
65的路径:6->5		权重:6
67的路径:6->5->3->7		权重:12
68的路径:6->5->3->7->8		权重:14

注意

1.迪杰斯特拉算法只支持非负权的图
比如上面的例子,如果B-D(-90),那么在遍历的时候,首先加入A进入已访问的数组里面,然后又将B加入到已访问数组里面,那么就会确定G到A最小距离是2,到B是3,而不是经过B-D这条线,迪杰斯特拉不会重新计算已经算好最小权重的节点,
2.节点之间不可达用Integer.MAX_VALUE代表
因为两个Integer.MAX_VALUE相加会溢出导致出现负权

猜你喜欢

转载自blog.csdn.net/qq_43416157/article/details/108811281