最短路径算法—Dijkstra算法详解

介绍

对于dijkstra算法,不少人可能感受熟悉而又陌生,可能大部分人比较了解bfs和dfs,而对dijkstra和floyd算法可能知道大概是图论中的某个算法,可是可能不清楚其中的做用和原理,又或许,你曾经感受它很难,那么,这个时候正适合你从新认识它。java

Dijkstra能是干啥的? node

在这里插入图片描述

Dijkstra是用来求单源最短路径的算法

就拿上图来讲,假如直到的路径和长度已知,那么可使用dijkstra算法计算南京到图中全部节点的最短距离。数组

单源什么意思?this

  • 从一个顶点出发,Dijkstra算法只能求一个顶点到其余点的最短距离而不能任意两点。

bfs求的最短路径有什么区别?spa

  • bfs求的与其说是路径,不如说是次数。由于bfs他是按照队列一次一次进行加入相邻的点,而两点之间没有权值或者权值相等(代价相同)。处理的更可能是偏向迷宫类的这种都是只能走邻居(不排除特例)。

Dijkstra在处理具体实例的应用仍是不少的,由于具体的问题其实带权更多一些。3d

好比一个城市有多个乡镇,乡镇可能有道路,也可能没有,整个乡镇联通,若是想计算每一个乡镇到a镇的最短路径,那么Dijkstra就派上了用场。code

算法分析

对于一个算法,首先要理解它的运行流程。 对于一个Dijkstra算法而言,前提是它的前提条件和环境:cdn

  • 一个连通图,若干节点,节点可能有数值,可是路径必定有权值。而且路径不能为负。不然Dijkstra就不适用。

Dijkstra的核心思想是贪心算法的思想。不懂贪心?blog

贪心算法(又称贪婪算法)是指,在对问题求解时,老是作出在当前看来是最好的选择。也就是说,不从总体最优上加以考虑,他所作出的是在某种意义上的局部最优解。 贪心算法不是对全部问题都能获得总体最优解,关键是贪心策略的选择,选择的贪心策略必须具有无后效性,即某个状态之前的过程不会影响之后的状态,只与当前状态有关。

对于贪心算法,在不少状况都能用到。下面举几个不恰当的例子!

打个比方,吃自助餐,目标是吃回本,那么胃有限那么每次都仅最贵的吃。

上学时,麻麻说只能带5个苹果,你想带最多,那么选五个苹果你每次都选最大的那个五次下来你就选的最重的那个。

不难发现上面的策略虽然没有很强的理论数学依据,或者不太好说明。可是事实规律就是那样,而且对于贪心问题大部分都须要排序,还可能会遇到类排序。而且一个物体可能有多个属性,不一样问题须要按照不一样属性进行排序,操做。

那么咱们的Dijkstra是如何贪心的呢?对于一个点,求图中全部点的最短路径,若是没有正确的方法胡乱想确实很难算出来,而且若是暴力匹配复杂度呈指数级上升不适合解决实际问题。

那么咱们该怎么想呢?

Dijkstra算法的前提

  1. 首先,Dijkstra处理的是带正权值的有权图,那么,就须要一个二维数组(若是空间大用list数组)存储各个点到达()的权值大小。(邻接矩阵或者邻接表存储)
  2. 其次,还须要一个boolean数组判断那些点已经肯定最短长度,那些点没有肯定。int数组记录距离(在算法执行过程可能被屡次更新)。
  3. 须要优先队列加入已经肯定点的周围点。每次抛出肯定最短路径的那个而且肯定最短,直到全部点路径肯定最短为止。

简单的归纳流程为

  • 通常从选定点开始抛入优先队列。(路径通常为0),boolean数组标记0的位置(最短为0) , 而后0周围连通的点抛入优先队列中(多是node类),并把各个点的距离记录到对应数组内(若是小于就更新,大于就不动,初始第一次是无穷确定会更新),第一次就结束了
  • 从队列中抛出距离最近的那个点B第一次就是0周围邻居)。这个点距离必定是最近的(全部权值都是正的,点的距离只能愈来愈长。)标记这个点为true而且将这个点的邻居加入队列(下一次肯定的最短点在前面未肯定和这个点邻居中产生),并更新经过B点计算各个位置的长度,若是小于则更新!
    在这里插入图片描述
  • 重复二的操做,直到全部点都肯定。
    在这里插入图片描述

算法实现

package 图论;

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

public class dijkstra {
	static class node {
		int x; //节点编号
		int lenth;//长度
		public node(int x,int lenth) {
			this.x=x;
			this.lenth=lenth;
		}
	}

	public static void main(String[] args) {
		 
		int[][] map = new int[6][6];//记录权值,顺便记录连接状况,能够考虑附加邻接表
		initmap(map);//初始化
		boolean bool[]=new boolean[6];//判断是否已经肯定
		int len[]=new int[6];//长度
		for(int i=0;i<6;i++)
		{
			len[i]=Integer.MAX_VALUE;
		}
		Queue<node>q1=new PriorityQueue<node>(com);
		len[0]=0;//从0这个点开始
		q1.add(new node(0, 0));
		int count=0;//计算执行了几回dijkstra
		while (!q1.isEmpty()) {
			node t1=q1.poll();
			int index=t1.x;//节点编号
			int length=t1.lenth;//节点当前点距离
			bool[index]=true;//抛出的点肯定
			count++;//其实执行了6次就能够肯定就不须要继续执行了 这句无关紧要,有了减小计算次数
			for(int i=0;i<map[index].length;i++)
			{
				if(map[index][i]>0&&!bool[i])
				{
					node node=new node(i, length+map[index][i]);
					if(len[i]>node.lenth)//须要更新节点的时候更新节点并加入队列
					{
						len[i]=node.lenth;
						q1.add(node);
					}
				}
			}
		}		
		for(int i=0;i<6;i++)
		{
			System.out.println(len[i]);
		}
	}
	static Comparator<node>com=new Comparator<node>() {

		public int compare(node o1, node o2) {
			return o1.lenth-o2.lenth;
		}
	};

	private static void initmap(int[][] map) {
		map[0][1]=2;map[0][2]=3;map[0][3]=6;
		map[1][0]=2;map[1][4]=4;map[1][5]=6;
		map[2][0]=3;map[2][3]=2;
		map[3][0]=6;map[3][2]=2;map[3][4]=1;map[3][5]=3;
		map[4][1]=4;map[4][3]=1;
		map[5][1]=6;map[5][3]=3;	
	}
}

复制代码

执行结果:

在这里插入图片描述
固然,dijkstra算法比较灵活,实现方式也可能有点区别,可是思想是不变的:一个贪心思路。dijkstra执行一次就可以肯定一个点,因此只须要执行点的总和次数便可完成整个算法。

欢迎感谢小伙伴点赞、关注,赠人玫瑰,手有余香!蟹蟹!

相关文章
相关标签/搜索