图的遍历
什么是图?图是由一些顶点和连接顶点的边组成的,例如上图就是由编号为1~5的五个顶点和(1-2,1-4,2-3,2-5,4-3,5-3,5-4)7条边组成的。而图又分为有向图和无向图。
那么什么是有向图呢?例如顶点2和3之间,2可以通过边2-3到达3,而3不可到达2,一般用箭头来表示。有了对有向图的描述,那么可以得知无向图就是两个顶点之间可通过一条边互相到达。如下图所示,2可通过一条边到达3,3也可以通过此边到达2。
图的存储
如何存储一张图?这里采用图的邻接矩阵法。下图二维数组中i和j表示顶点i到顶点j是否有边。1表示有边,∞表示没有边,设自己到自己的距离为0。(这里设权值即边的长度为1)
深度优先遍历
- 以未被访问的顶点为起始点,沿其边走到未被访问过的顶点。
- 当没有未被访问过的顶点时,则回到上一个顶点,继续遍历其他顶点。
现在从顶点1开始遍历,使用深度优先遍历会得到以下结果:
从图中可以看出有红色和蓝色两条路径,那么它们是怎么得出来的呢?假设以1为起始点(表示1已访问)开始遍历,顶点1有1-2,1-4两条边,首先选择1-2这条边将2访问。然后以2为起始点有2-3,2-5两条边,选择2-3这条边将3访问。再以3为起始点发现没有边可以到达其他顶点,此轮遍历到达最深处,退回上一个起始点2,从另一条边2-5开始遍历将5访问。再以5为起始点将4访问,此轮遍历到达最深处,逐一返回发现其他顶点都已访问,深度优先遍历结束。
广度优先遍历
- 以未被访问的顶点作为起点,访问其所有相邻的顶点。
- 对每个相邻的顶点,再访问它们相邻的顶点,直到所有顶点被访问。
使用广度优先遍历会得到以下结果
假设以1为起始点,对它的两条边1-2,1-4进行扩展将2,4访问。然后以顶点1的第一条边访问到的顶点2为起始点对两条边2-3,2-5进行扩展,将3和5访问。再以顶点1的第二条边访问到的顶点4为起始点进行扩展,顶点4没有边不做操作。再以顶点3为起始点扩展,顶点3没有边不做操作。随后是顶点5,顶点5有边而顶点4已访问。所有顶点已访问到此广度优先遍历结束。
最短路径
Floyd-Warshall--求任意两点之间的最短路径(多源最短路径)
允许经过1~n号所有顶点进行中转,求任意两点间的最短路径。
假设允许经过1号顶点,只需判断e[i][1]+e[1][j]是否比e[i][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];
Dijkstra--求一个点到其余各点的最短路径(单源最短路径)
这里先使用数组dis存放起始顶点到其他顶点的初始路程
- 找未知路径中的最小值(距离起点最近的顶点)
- 将该点放入已知的最短路径中(dis数组)
- 从该点开始对每一条边松弛,以u为始边,如果存在一条边v使u->v(dis[u]+e[u][v])小于dis[v]则用新值代替dis[v]。
- 最后dis存放的就是1到各点的最短距离
核心代码
for (i = 1; i <= n - 1; i++)
{
//找距起始点最近的顶点
min = inf;//inf为设置的无穷值
for (j = 1; j <= n; j++)
{
if (book[j] == 0 && dis[j] < min)
{
min = dis[j];
u = j;
}
}
book[u] = 1;//将u标记为1放入最短路径
for (v = 1; v <= n; v++)
{
if (e[u][v] < inf)//小于无穷
{
if (dis[v] > dis[u] + e[u][v])
dis[v] = dis[u] + e[u][v];//用新值代替dis[v]
}
}
}
刷题截图