超详解析,手打不易,转载注明出处,点赞收藏,多谢支持
存储方式 :邻接表
有两个集合
- S :已得出与V(起始点)最短路径的顶点结合
- U :S的反集,初始时,所有顶点都在此集合
三个辅助数组:
- S[K]: 标记 k 顶点与V(起始点)是否连通
- Path[K] : 标记顶点 K 的前驱顶点
- Dist[K] : 标记 V 到顶点 K 的最短路径(权值)
算法步骤:
- 初始化:将V加入S集合中,并求出与V直接相连的各顶点权值,将这些与V相连的顶点前驱设为V顶点
- 循环遍历所有顶点,找到还没确定最短路径,且Dist 最小的顶点K,令S[K]=ture。 并检查所
有邻接自K的顶点,对于邻接自K的顶点X,若S[X]==false 且 Dist[K] + K到X的权值 < Dist[X],则Dist[X] = Dist[K] + K到X的权值
结果演示
有向图,C++实现
#include<iostream>
using namespace std;
#define MVNum 100 //最大顶点数
#define MaxWeight 32425 //最大权值
typedef char VerTexType; //顶点的数据类型为char
typedef struct ArcNode{ //边结点
int adjVex; //该点所指向的顶点位置
struct ArcNode *nextArc; //指向下一条边结点的指针
int info; //和边相关的信息:可用于带权图
}ArcNode;
typedef struct VNode{ //顶点信息
VerTexType data;
ArcNode *firstArc; //指向第一条依附该节点的边的指针
}VNode,AdjList[MVNum]; //AdjList:表示邻接表类型
typedef struct{
AdjList vertices; //顶点表
int vexnum,arcnum; //图的当前点数和边数
}ALGraph;
void CoutResult(ALGraph,int,bool[],int[],int[]); //输出结果
/*根据顶点名 定位在vertices表中的数组下标*/
int LocateVex(ALGraph G,char v){
for(int i=0;i<G.vexnum;i++){
if(G.vertices[i].data == v)
return i;
}
return -1;
}
/*采用邻接表表示法,创建无向图G*/
bool CreateUDG(ALGraph &G){
cout << "输入总顶点数、总边数:" << endl;
cin >> G.vexnum >> G.arcnum; //输入总顶点数、总边数
for(int i=0;i<G.vexnum;i++){
cin >> G.vertices[i].data;
G.vertices[i].firstArc = NULL;
}
cout << "请输入相互连接的v1、v2、权值:" << endl;
char v1,v2; //顶点v1、v2
int w,k,j; //w表示v1-v2的权值
for(int i=0;i<G.arcnum;i++){
cin >> v1 >> v2 >> w;
k = LocateVex(G,v1);
j = LocateVex(G,v2);
/*头插法,给顶点i的第一条边赋值*/
ArcNode *p1 = new ArcNode;
p1->info = w;
p1->adjVex = j; //邻接点序号为j
p1->nextArc = G.vertices[k].firstArc; //将新结点p1插入顶点vi的边表头部
G.vertices[k].firstArc = p1;
}
return true;
}
/*获取两点权值*/
int getBothWeight(ALGraph &G,char X,char Y){
int Y_sub = LocateVex(G,Y);
int X_sub = LocateVex(G,X);
if(Y_sub == X_sub) //为同一点
return 0;
ArcNode *p = G.vertices[X_sub].firstArc;
while(p){
if(p->adjVex == Y_sub) //找到 -> 返回权值
return p->info;
p = p->nextArc;
}
return MaxWeight; //未找到 -> 返回最大值
}
/* 获取Dist数组内(未加入 S 集合,即S[i] == false)最小权值的下标 */
int getMinDist(int len,bool S[],int Dist[]){
int min = MaxWeight;
int min_sub = -1;
for(int i=0;i<len;i++){
if(!S[i] && Dist[i] < min){
min = Dist[i];
min_sub = i;
}
}
return min_sub;
}
/*Dijkstra:迪杰斯特拉!*/
void shortTestPath_Dijkstra(ALGraph &G,char V){
int n = G.vexnum;
bool S[n] = {false}; //结点在S集合,则已生成最短路径
//该结点的上一个结点下标,初始值 -1 为不连通,没有上一个连接点
int Path[n];
int Dist[n] = {MaxWeight}; //从V到各顶点的权值,MaxWeight代表没联通
//将原点加入到S集合中
int v0 = LocateVex(G,V); //V结点的下标
//n个顶点依次初始化
for(int i=0;i<n;i++){
char Y = G.vertices[i].data; //要检查的顶点 Y
Dist[i] = getBothWeight(G,V,Y); //获取 V -> Y 的长度
//若不是最大值,代表 V 与 Y 有直接连通的路线,更新Path数组
if(Dist[i] < MaxWeight)
Path[i] = v0; //该节点的前驱为v0
else
Path[i] = -1; //与V结点不连通
}
S[v0] = true;
Dist[v0] = 0; //原点到原点的距离为0
/*--初始化结束,开始主循环,每次求得v0到某个顶点k的最短路径,将K加到S集合--*/
for(int i=1;i<n;i++){
int k0 = getMinDist(n,S,Dist); //获取Dist数组中没加入S集合的顶点 最小的权值
if(k0 == -1) //已找完 -- 剩下的为与 V 不连通的结点
break;
char K = G.vertices[k0].data; //最小权值连接的顶点名称 K
S[k0] = true; //将该顶点加入到S集合中
//更新从v0出发到集合V-S上所有顶点的最短路径长度
for(int j=0;j<n;j++){
if(!S[j]){
char Y = G.vertices[j].data; //要检查的顶点
int w = getBothWeight(G,K,Y); //获取 K -> Y 的长度
//若从中转顶点K到该顶点Y的路径要小 -> 修改
if(Dist[k0] + w < Dist[j]){
Path[j] = k0; //该顶点的前驱更改为 k0
Dist[j] = Dist[k0] + w; //最短路径更新!
}
}
}
}
//输出
CoutResult(G,n,S,Path,Dist);
}
//输出结果
void CoutResult(ALGraph G,int n,bool S[],int Path[],int Dist[]){
cout << "集合:\t\t";
for(int i=0;i<n;i++)
cout << S[i] << "\t";
cout << endl;
cout << "前驱:\t\t";
for(int i=0;i<n;i++)
cout << G.vertices[Path[i]].data << "\t";
cout << endl;
cout << "到V的最短权值:\t";
for(int i=0;i<n;i++)
cout << Dist[i] << "\t";
cout << endl;
}
/*
Data(测试数据):
6 8
A B C D E F
A C 10
A E 30
A F 100
E D 20
E F 60
D F 10
C D 50
B C 5
*/
int main(){
ALGraph G;
if(CreateUDG(G)){
cout<< "下标\t元素:\n";
for(int i=0;i<G.vexnum;i++){
cout<< i << "\t" << G.vertices[i].data << ":\t";
ArcNode *p = G.vertices[i].firstArc;
while(p){
cout << "->" << p->adjVex << "\t";
p = p->nextArc;
}
cout << endl;
}
}
shortTestPath_Dijkstra(G,'A');
}