Dijkstra算法,Floyd算法,Prim算法,Kruskal算法

Dijkstra算法是贪心算法,是一个单源最短路算法,就是先记录下从起始点到各个点的距离,然后选择距离最小的结点,用这个最小距离加上选取的点和其他点一一比较,看看需不需要更新起点到其他点的距离,直到所有的点选完。

疑问:为什么要先选最小的点?

按下图排

如果先取小的排先是  #,10,#,30,100(把#当成是无穷大)

第二个结点的10最小,更新

得 #,10,60,30,100

接着是第四个结点30最小,更新

得 #,10,50,30,90

接着是第三个结点50最小,更新

得 #,10,50,30,60

最后由v5无更新,最终得#,10,50,30,60

如果取最大会怎么样呢?

原始序列:  #,10,#,30,100(把#当成是无穷大)

取100,无更新

取30,由v4更新,得:

#,10,50,30,90

咦,我们发现在这一步就出现了问题,

首先是多了50,比30大这就不符合取最大的规则了,当然这不是最重要的,你可能会说,我可以选回去麻,但是先不说这在算法上可不可以实现,它就算可以实现也是复杂的。

其次,第一步取最大这个情况下后面100变小了,它因为用后面的v4更新后变成了90,这会出现一种什么样的问题呢?这会让第一步的比较无效,试想会不会因为v0到v5的路径变小了,导致v0到某个结点的距离要大于v0到v5再到某个结点的距离呢?这是有可能的,当然在这个图里面没有这样的结点,但这是有可能的,对吧?

这就是错误的点了。

小朋友,相信这时候你又有了疑问,什么疑问呢?那便是,为什么从最大更新后原来到最大距离的那个结点的距离会变小呢?

有点饶,大概知道意思就好啦。

这便是因为有别的结点和源点的距离更小啊,在加上别的结点到v5结点的距离,是不是v0到v5的距离有可能会更小,有没有这种可能,有吧?

而换了从距离最小的那个结点开始比较更新,就不会有这个问题啦。

因为你想啊,如果别的结点和源点的距离更大,在加上别的结点到v5结点的距离,是不是v0到v5的距离不可能更小,不可能,对吧?这就和前面的选择不冲突了。

我想到了一个有趣的事情,如果要求当源最长路,大概就是这样反过来吧。

Floyd多源最短路算法

假如现在只允许经过1号顶点,求任意两点之间的最短路程,应该如何求呢?只需判断e[i][1]+e[1][j]是否比e[i][j]要小即可。e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。其中i是1~n循环,j也是1~n循环,代码实现如下。

for (i = 1; i <= n; i++)

            {

                for (j = 1; j <= n; j++)

                {

                    if (e[i][j] > e[i][1] + e[1][j])

                        e[i][j] = e[i][1] + e[1][j];

                }

            }

接下来继续求在只允许经过1和2号两个顶点的情况下任意两点之间的最短路程。如何做呢?我们需要在只允许经过1号顶点时任意两点的最短路程的结果下,再判断如果经过2号顶点是否可以使得i号顶点到j号顶点之间的路程变得更短。即判断e[i][2]+e[2][j]是否比e[i][j]要小,代码实现为如下。

//经过1号顶点

for(i=1;i<=n;i++) 

for(j=1;j<=n;j++) 

if (e[i][j] > e[i][1]+e[1][j])  e[i][j]=e[i][1]+e[1][j]; 

//经过2号顶点

for(i=1;i<=n;i++) 

for(j=1;j<=n;j++) 

if (e[i][j] > e[i][2]+e[2][j])  e[i][j]=e[i][2]+e[2][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];

这段代码的基本思想就是:最开始只允许经过1号顶点进行中转,接下来只允许经过1和2号顶点进行中转……允许经过1~n号所有顶点进行中转,求任意两点之间的最短路程。用一句话概括就是:从i号顶点到j号顶点只经过前k号点的最短路程。

另外需要注意的是:Floyd-Warshall算法不能解决带有“负权回路”(或者叫“负权环”)的图,因为带有“负权回路”的图没有最短路。例如下面这个图就不存在1号顶点到3号顶点的最短路径。因为1->2->3->1->2->3->…->1->2->3这样路径中,每绕一次1->-2>3这样的环,最短路就会减少1,永远找不到最短路。其实如果一个图中带有“负权回路”那么这个图则没有最短路。

看了这里,首先有一个疑问:怎么这个三重循环的两个节点之间只更新了1次?只能有两段边或者是一段边的情况么?显然不是的。

为什么不是呢?我们先一个个分析,当k中转站只有一个的时候,显然是这样的。但是当k有多个的时候就不是这样子的了。

为啥不是呢?我看着这个循环就像只更了一次啊。

小朋友,那你有木有注意到它里面也是有两个循环滴,好像不好理解喔。

更新就更新,你用两个循环是啥意思呢?

这就是问题的切入点了。

这我们只能从每一个循环开始看,假如只允许一个结点进行中转,e[i][j]表示的是从i号顶点到j号顶点之间的路程。e[i][1]+e[1][j]表示的是从i号顶点先到1号顶点,再从1号顶点到j号顶点的路程之和。而两重循环把n个顶点之间的联系都遍历了一遍,可以得出是否需要经过中转最短。

我们可以假设有4个顶点,v1,v2,v3,v4.。

把v1看成是中转的顶点v2到v3的最短路为2 1 3

v2到v4的最短路为2 4

如果把v3变成是可以中转的顶点,是不是有这样子的可能,v2到v4之间的最短路可以是 2 1 3 4,有这种可能,对吧?

那这在两个两重循环里面是怎么体现的呢?一个两重循环可以理解,那两个,n个要怎么理解呢?

从两个开始说起吧,我们需要理解两个顶点之间是怎么出现3条边和两个顶点的。

以我们上面说的四个结点为例子,首先,把v1当成是中转站,e[2][3]=e[2][1]+e[1][3],没问题,对吧。

进入到第二个二重循环,把v3加入作为中转站,e[2][4]=e[2][3]+e[3][4],再把e[2][3]展开就对了。

这就是最外层循环和里面两重循环的联系了,外面的循环将里面的两重循环后的值发生了变化,一直变化着,一步步迭代更新。

小朋友,你好像又有了新的疑问,那个,为什么单源最短路需要从和源点距离最短的的结点开始选择,而这种算法却不用把中转结点也这样子选更新的结点呢?

哎,其实是不一样的,哪里不一样呢?

首先,你有没有发现,Dijkstra算法里面的v0(源点)并不是中转点,Dijkstra算法的中转点是从和源点距离最短的的结点开始递增地一个个选择的,这就让它的中转点不能乱选,不然可能前面的选择就无效了(前面已经分析过了)。

而为什么Floyd算法就可以乱选呢?首先它的中转点是确定的,再随意选择源点和终点,当也不是随意的,因为最后它都会把所有的源点和终点通过两重循环选完。

而它们两两之间在每个二重循环里面是不冲突的,因为只有一个中转点。

如果加入了中转点,它也是不冲突的,它一遍遍地更新了最短路。

你可能会想,你前面举的例子的终点改变了(把3改成了4),那如果不变呢?

好,我们来看不变的情况。我们用v3作为第一个中转的结点

e[2][7]=e[2][3]+e[3][7]假设更新了,经过v3是2到7的最短路。

再加上v4作为中转,我们发现它也是不冲突的。

我们可以对e[3][7]进行更新,也可以对e[2][7]=e[2][4]+e[4][7]进行更新。

哇,你可能会想,哇,它变了,可能冲突了哇。

可系,真的吗?

反正怪怪的。

其实没有的,先是可能e[2][7]=e[2][3]+e[3][7],然后可能更新为e[2][7]=e[2][4]+e[4][7]

冲突在哪里?没有吧,这个算法只是穷举了所有的情况。

你可能还有一个问题,这是什么破算法,我直接用Dijkstra算法遍历n次来不就完了吗?

额,问题是不一定好啊,而且想把复杂度降下来,程序写起来还蛮复杂的。后面有时间再想吧。

好了,吐血三升,一个三个循环的算法,居然想了半天,罢辽,罢辽,还有问题也不想辽。

注意:如果两个顶点之间没有边的话,应该定义为正无穷,后面才能慢慢变小。


Prim算法也是贪心算法来着,是从顶点开始选取的,这和kruskal算法有很大区别。



Kruskal算法,将森林合并成树。起始的每个结点它也当成是一个树。先把所有权重的边都选出来(因为是森林,所以不能只取一边) ,不构成回路就行,直到    V-1条边和边集合中没了元素,构成回路的边扔掉。

简单说,就是不断地把最小边选出来,并且不能构成回路,最后选到V-1条边就没了,再选就一定会构成回路。这就能将多个结点并成一棵树了。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355

推荐阅读更多精彩内容