春季开学第一篇(Graph图+Floyd - Warshall 多源最短路径 + Dijkstra单源最短路径 )

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

有一部分的内容是寒假博客的延申


一、关于图的各自术语、概念

图的定义:

一个图(一般记作 G {\displaystyle G} G)由两类元素构成,分别称为“顶点”(或节点、结点)和“边”。每条边有两个顶点作为其端点,我们称这条边“连接”了它的两个端点。因此,边可定义为由两个顶点构成的集合 ----维基百科

通俗地说:许多的点用线连起来 所构成的图形。

在这里插入图片描述有向、无向图:连起节点的线段,可以是有方向的,也可以是无方向的。
上图就是无向图(默认两边都可以到达),加个特定的箭头就是有向的。

在这里插入图片描述加权图:图的边可以赋予各自具体的长度,这就是各自的“权重”
在这里插入图片描述

二、存储图的数据结构

1.二维数组(最基本的方法)

map[a][b] 代表从a点到达b点是否有路
1为可以到达,0为不可达到。

先忽略图中的箭头,看成是无向图。
在这里插入图片描述举例:2点与4点是联通的,那么map[2][4]就为1(因为无向,所以map[4][2]也是一样的)。

在这里插入图片描述不难发现,无向图得到的二维数组是以对角线对称的。
毕竟map[a][b]和map[b][a]一样。

接下来看一个有向图 在这里插入图片描述
那么得到的就(基本上)不会是对称的了在这里插入图片描述-----图出自《啊哈!算法》

一般像图里的都做如下规定:
map[a][b] 是从a到b
反过来map[b][a]从b到a
毕竟是有向图,所以就不会像之前那样对称了。

三、一些具体问题和算法

1.多源最短路径问题

问题先来个简单的情况:
在这里插入图片描述(我自己画的草图)
A、B两城之间的道路如下,其中很明显可以看到
A可以直接到B,但是绕弯路很远。
A也可以经过C城到达B城,很明显虽然不是直达但是路程近了。
生活经验告诉我们肯定优先选择A-C-B
但是计算机不知道啊!我们如果存放数组肯定就是:
map【A】【B】,map【C】【B】,map【A】【C】
如此存放,哪怕我们知道实际上AC+CB<AB,但是计算机不知道啊!
所以这个算法的任务,就是学会—“看起来绕路、实际上路程更短”
绕路,这个简单的例子中绕的是C。
而真正的问题中可能会有很多的城市,像C这样被绕反而路程短了。
这么些个C,就是**“多源最短路径问题”“源”**
可能还有D城,让A、B更短,这时候C城我们又不要了;有E、F…
一个个找,直到找到最短的。

当然,“改编不是乱编,戏说不是胡说” ,绕路也不是看见个城市就绕,至少你得保证C城同时和A、B都联通吧!比如:
在这里插入图片描述这咋办???
想绕也饶不了。
没路可走肯定不行,有路就一定可以吗?

在这里插入图片描述这种呢?AC有路啊!那map【A】【C】就可行嘛?
不对吧!C—>A可以,A—C 可没路!注意箭头!
所以我们在构建二维数组的时候,就得注意这类情况
map【A】【C】=9999999(即无限大)。


通俗例子讲完了,就来说说严谨的解释。

是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存在负权回路)的最短路径问题 ----维基百科

《啊哈!算法》 此图是《啊哈!算法》的例子
在这里插入图片描述这是根据图上的直接路径得到的路径(即未经过优化的)
现在只允许经过1号顶点,求任意两点的最短路径
**此时我们只需判断e[i][1] + e[1][j] 是否比e[i][j] 要小即可。

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];
        

代码很简单,遍历二维数组,然后一个个尝试,看i–>j这个路径可否被1号点优化。即:(1号点帮得上忙就来帮)
在这里插入图片描述这时候我们发现,虽然不是全部路线都派上用场了,但是至少也有所收获。

1号点的完成了,那在此基础上,二号点也来帮忙,所以在此前基础上,把二号点也加进来。

//经过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];

在这里插入图片描述嗯,又好了些。
那么接下来,让三号、四号节点也来帮忙的过程就自然不必多说了。

然后,把1、2、3、4节点 都来帮忙 的过程压缩到一个for循环里,就算是大功告成啦~~~

for(int k = 1 ; k <= n ; k ++)
        for(int i = 1 ; i <= n ; i ++)
                for(int j = 1 ; j <= n ; j ++)
                        if(e[i][j] > e[i][k] + e[k][j])
                                e[i][j] = e[i][k] + e[k][j];

k代表几个节点来帮忙
i、j用来遍历下标。

总结

优点:比较容易容易理解,可以算出任意两个节点之间的最短距离,代码编写简单。
缺点:时间复杂度比较高(n3),不适合计算大量数据,当数据稍微大点儿的时候就可以选择其他的算法来解决问题了,不然也会是超时。

2、单源最短路 Dijkstra

话接上文说到最短路径,上次是任意俩点都得给他优化。那这次我们指定一个顶点,以他为C位 ,就想知道他到剩下其他顶点的最短路径,用floyd好像duck不必 O(n³)的时间复杂度,这谁顶的住啊

所以可否空间换时间呢?可以!接下来请出来Dijkstra算法!

在这里插入图片描述老规矩,先上图。这个图,咱们就以一号点为源点,找出剩下其他点,各自与1点的最短路径。
在这里插入图片描述
先存储一下图中的信息,老规矩e【A】【B】是A–>B的距离。
在这里插入图片描述
在这里插入图片描述
dis[2],就是目前已知的最短的从1到2的路径。

目前已知1–>2 和1–>3,选小的那个:1–>2.
然后找2可以到4点和3点。即2–>4,2–>3也已知了
这时候发现3点好像有两条路:1–>3 和1–>2–>3,依然选择小的那个,
刚才4点也确定了。
到此为止,2的所有出度都已经结束,那么就在已知到1号点距离的点中,找离一号点最近的那个点发起新一轮拓展攻势—4号点目前离一号点最近,就决定是你了!

4–>34、4–>513、4–>615都确定了,找最小的那个!
4–>3
4最小!开始比对原来的1—>3,嗯短了些!更新数据!
4–>513次之!开始比对原来的1–>5,好像没变短,嗯不更新!
4–>6
15最后,之前6号点默认为∞(毕竟没到达过嘛,无历史记录 ),那就勉为其难的先更新一下吧!1—>6==19

四号点的历史使命也完成啦!再一次看谁(2、4都完成使命了,就不管了)离1最近。周而复始~

到最后所有的点都会更新好,这时候,1到所有点的最短距离都有啦~!

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值