1.问题的引入:
- 在一个图中,如何根据图中的信息,从一个单一的顶点出发,寻找其他所有顶点到这个顶点的最小值呢?并且还要求最小路径是什么。
2.算法思想:
- 辅助定义:
- 引入两个集合S 和V−S, 其中第一个集合用于记录已经收录的顶点(亦即已经求出最短路径的节点集合)第二个集合是该图中剩余没求得最短路径的节点集合。
- 引入辅助向量dist[Vertax]该向量用于记录每一个顶点对应的dist值
- 那么有小伙伴要问了:什么是dist值呢?:简单来说就是只经过集合S中的所有顶点从原点出发到这个点的最短距离叫做这个节点的dist值
- 由此试想一下,当所有节点都收录进集合S中,是不是就意味着已经求的所有顶点的最短路径了呢?
- 引入辅助向量path[vertax],该向量用于记录这个顶点到原点的最短路径中途中经过的顶点
- 什么意思呢?简要回答一下,就是说比如一个顶点v最短路径中前一个顶点是w,则path[v]=w.
- 为什么要这么做呢?因为比如已经求得所有节点的最短路径值,通过path[i]找i节点路径中前一个节点,以此直到找到原节点,以此压入栈中,然后我们从栈中以此取出这些节点对应就是i这个顶点到原点的最短路径了
- 算法概述:
- 首先将原点v0收录进S集合中,于是dist v0=0这个很好理解,自己到自己的距离等于0嘛
- 重复如下过程直到在未收录节点集合V- S中找不到dist最小的值:
- 找到dist最小值的节点并且记录其信息为v
- 然后将这个节点v收录进入S中,亦即该节点的最短路径是已经求出。为什么可以这样做?
- 假设最小dist对应节点i的dist值不是最短路径,那么必然存在其他一个节点j在V- S这个集合中,在i的最短路径上使得S中节点先到j再到i才是最短路径。但是问题来了如果该假设成立,就意味着j的dist值一定是小于i的,因为我们一直是取的未收录中最小的,那么j理应加入S集合了,但是j却不在S集合中。与假设矛盾
- 由上述分析,每一次找到最小的dist对应的节点i我们可以放心大胆的说这个节点dist就是最短路径,因为我们按照非递减的顺序收录节点
- 对于刚刚收录节点v,看一圈其临边对于的节点w,如果dist[v]+Edge(v,w) <dist[w],更新dist[w]=dist[v]+Edge(v,w)
- 关于这一步也好理解,刚刚收进去一个节点了,如果通过这个节点能够使得到相邻的其他节点的值变小,当然也就以为这其他节点的dist值要更新(关于这一点去理解一下dist定义)便很好掌握
- 算法伪代码:
-
bool FindMin(int *d,bool *c,int& v,int length) //该函数是在所有还未被收录的定点中找到最小值d,然后将该节点记录在v,并返回成功与否 { int Max=InF+1; for (int i = 0; i <length ; ++i) { if (!c[i]&&d[i]<Max) { v=i; Max=d[i]; } } if(Max!=InF+1) return true; else return false; } bool DjsTra(AdjGraph& G,int v0,int* path,int* dist) { //初始化路径 for (int i = 0; i <G.num_V ; ++i) { path[i]=-1; } //初始化 dist数组 for (int i = 0; i <G.num_V ; ++i) { dist[i]=InF; } //已经收录的节点集合 bool collected[G.num_V]; for (int i = 0; i <G.num_V ; ++i) { collected[i]= false; } int count=0; dist[v0]=0;//从v0开始收录节点; int v_next; while (1) { if (!FindMin(dist, collected, v_next,G.num_V)) { break; } else { v_next=true; for (ArcNode* p=G.Adjlist[v_next].FirstEdge; p ; p=p->Next) { if(dist[v_next]+p->info<dist[p->vertax]) { dist[p->vertax]=dist[v_next]+p->info; path[p->vertax]=v_next; } }//对于刚刚收录进去的节点的所有临街点看看能不能更新最小路径 } } if (count<G.num_V) return false;//图不是联通图 else return true; //函数调用完成,最短路径在dist 数组中,路径上的节点保存在path数组中 }