目录
下一篇:有向无环图DAG
单源最短路径和每对顶点间的最短路径
一、BFS求无权图的单源点最短路径
原理:从源点开始BFS遍历,按BFS的规则,将每个被遍历到的顶点(且没被访问过)的距离设为上一个顶点的距离值+1
代码(伪):
//求u到其他顶点的最短路径,G为无权图
void BFS_MIN_Distance(Graph G,int u){
//数组d代表从u到i的最短路径,初始化为无限大
//path代表是从哪个顶点过来的,初始化为-1
for(i=0;i<G.vexnum;i++){
d[i]=inf;
path[i]=-1;
}
//从u开始当然u到u距离是0
d[u]=0;
//标记数组
visited[u]=TRUE;
//u入队
EnQueue(Q,u);
while(!isEmpty(Q)){
DeQueue(Q,u);//队头元素出队
//循环遍历顶点u的下一个相邻顶点
for(w=FirstNeighbor(G,u);w>=0;w=NextNeighbor(G,u,w)){
if(!visited[w]){
//若没有被访问过,则把源点到上一个顶点的距离+1给当前顶点
d[w]=d[u]+1;
path[w]=u;
visited[w]=TRUE;
EnQueue(Q,w)''
}
}
}
}
也可以使用广度优先生成树,来看该顶点在第几层,距离就是几
二、迪杰斯特拉,带权图的最短路径
源点为v0的图
使用3个数组
先处理源点,将final表中v0设为最短,dist[v0]=0,path中将v0相邻的顶点设为0,表示他们的前驱顶点为v0
按以下规则执行
1、从dist表中距离最短的且没有被找到最短路径的顶点(final中为false的顶点)
2、从该顶点出发更新(计算其相邻顶点的当前最短路径即通过比较新旧距离大小)其相邻顶点(相邻顶点中final为false的)的距离和path表中的前驱顶点,最后将final表中该顶点改为已找到最短路径
3、重复1、2直到final表中全为已找到
例:处理v0后的第一个顶点v4
经过1、2后
接下来肯定是操作V3这里不再赘述
通过path数组找到路径
int v=path[v];
v_path[vexnum];
int count=0;
while(path(v)!=-1)
{
v_path[count]=v;
v=path[v];
count++;
}
时间复杂度分析
每次都要扫描找到最小的distance值,进行n-1轮
无论是邻接矩阵还是邻接表都需要O(n^2)或O(|V|^2)
对于负权值带权图
迪杰斯特拉算法就无法作用了
例如:
解决方案,将所有权值做一个平移,使得所有权值都是正数
负权值应用举例:在计算某些可以补充或‘节省’的值时,比如骑电动车(初始12格电)从A到B要消耗5格电剩余7格,但是从A到C再到B,虽然到C要消耗6格电但是可以充到15格电,计算得出A->C->B最后电动车剩余10格电。
三、弗洛伊德(Floyd)算法
动态规划
对于该矩阵创建两个二维数组
A(-1)(各个顶点间的最短路径长度)和path(-1)(两个顶点之间的中转点)
#0(阶段0):允许在V0中中转,求最短路径?——求A(0)和path(0)
通过如下规则:
对阶段k
若A(k-1)也即上一个A中两个顶点间的最短距离大于当前通过V0中转到达的最短距离(A(k-1)[i][j]>A(k-1)[i][k]+A(k-1)[k][j]),则更新两顶点间的最短路径距离(A(k)[i][j]=>A(k-1)[i][k]+A(k-1)[k][j]),将中转矩阵中两个顶点的中转顶点标记为中转顶点Vk,每个阶段都扫描全部的元素看是否满足上面的关系并更新
例如阶段0
经过n(顶点个数)轮递推后得到A(n-1)和path(n-1)
最终
就是通过前一个矩阵的状态推出下一个矩阵的状态
由于这个例子过于简单,引起了我更多的思考,若有两个以上的中转点,这个算法会怎样处理?
例如:
边(顶点->另一个顶点) | 权值 |
0->1 | 5 |
1->2 | 5 |
2->3 | 5 |
显然初始时1->2为无穷,1->3也是无穷,要使得能1->3得通过1与2进行中转
则通过阶段0,顶点0作为中转点无法更新任何路径
阶段1,通过顶点1中转,可更新0->1->2(即0->2) 为10替换掉原来的∞
阶段2,通过顶点2中转,可更新0->1->2->3(即0->3)为15替换掉原来的∞
阶段3,显然无法做任何更新
至此floyd算法完成执行
若要得到顶点0到3中所有经过的路径,可以查path矩阵
首先可查0->3通过2中转,再查0->2通过1中转,再查0->1没有中转即path表中的-1,即得出所有路径,对于更复杂的例子可以使用递归来实现
例如:
v0->v2->v1->v3->v4
若根据path可得v0-->v3>v4
分别将v0->v3和v3->v4展开
得到v0->v2,v2->v3,v3->v4
继续展开得:v0->v2,v2->v1,v1->v3,v3->v4
最终得到结果(即对每对顶点都进行展开)
代码实现:
//Floyd算法伪代码实现
//初始化部分根据图的信息初始化A和path矩阵...
for(int k=0;k<n;k++){//k个阶段,选第k个顶点作为中转顶点
for(int i=0;i<0;i++){//遍历矩阵中每一个元素,需要双重循环
for(int j=0;j<n;j++){//注:A[i][j]代表顶点
if(A[i][j]>A[i][k]+A[k][j]){//如果通过k中转的路径更短则,i与j的距离更新为新距离
A[i][j]=A[i][k]+A[k][j];//更短的路径长度
path[i][j]=k;//中转点
}
}
}
}
时间复杂度为O(|V|^3)
空间复杂度为O(|V|^2)
Floyd可以解决带负权值的图
但解决不了带负权回路的图(可能没有最短路径的图)
不难发现每经过一次回路,路径长度就会-2