Dijkstra算法的深入理解与优化改进
Dijkstra算法的深入理解
1. 问题提出
dijkstra算法主要为了解决单源最短路径问题:给定带权有向图(带权代表着我们可以针对不同的情况对路径上的权值进行调整,以得到最符合当前需求的路径,有向则表明它要求解的路径有源点和目的点,还有它是一个图)G和源点V,求从 V 到 G 中其余各顶点的最短路径。
2. 求解过程
1.将图中的节点分为两组,一组为已求出最短路径的节点集合,另一组为还未求出最短路径的节点集合。(这有点类似与动态规划的思想,利用已知最优解,求解后续最优解)
2.按照各节点与V的最短路径长度递增的顺序,逐个将还未求出最短路径的节点加入到已求出最短路径的集合中(还未求出最短路径的节点到V的路径是不断变化的,但是未求出最短路径集合中当前距V最近的节点的路径已经可以确定是其最短路径,因为未求出最短路径的集合中其他节点到V的路径之所以会变,是因为把当前最短加入到已求出集合后对其造成的影响)
3.代码分析与实现
根据上面的求解过程,假设我们已经有了用邻接表或邻接矩阵保存的节点间路径信息,我们需要一个数组来保存bool值,即节点是否已确定最短路径,一个数组来保存还未确定最短路径的节点,若我们需要得到中间路径,则还需要一个数组来保存path路径(即其前驱节点),上述三个数组的大小均为结点的个数。
typedef struct ArcNode //代表一条边
{
int adjvex; //边指向的节点
struct ArcNode * nextarc;//下一条边
int weight;//权重
}ArcNode;
typedef struct VNode
{
char *name; //节点的名字,标识
ArcNode * firstarc; //指向可达的第一条边的指针
}VNode;
typedef struct
{
VNode vertices[num]; //图中的节点数组
int vexnum, arcnum; //图中节点的数目,边的数目
}MyGraph;
void DIJ(MyGraph &a, int v, int *lens, int *paths)
{
int n = a.vexnum;
int *temp = new int[n];//保存临时最短路径
const int max = ~(1<<31);
//初始化
for(int i=0; i<n; i++)
{
//lens表示节点是否已确定最短路径,本来可以是一个bool的数组,
//这里用-1表示未确定,确认后用实际的最短路径进行填充。
lens[i] = -1;
temp[i] = max;//初始所有节点不可达
paths[i] = -1;//保存源节点到目的节点的路径上,目的节点的前一个节点
}
ArcNode *p = a.vertices[v].firstarc;
//使用源节点的邻接边初始化一部分路径长度以及paths
while(p)
{
temp[p->adjvex] = p->weight;
paths[p->adjvex] = v;
p = p->nextarc;
}
lens[v] = 0;
paths[v] = 0;
//开始主循环,每次求得源点到某个顶点的最短路径,并将此节点加入到已确定最短路径的集合中
for(int i=1; i<n; i++)
{
int index = -1;
int min = max;
//选择一条当前最短路径
for(int j=0; j<n; j++)
{
if(lens[j] < 0 && temp[j] < min)
{
min = temp[j];
index = j;
}
}
if(index == -1)//若没有可达路径,则退出
break;
lens[index] = min;//设置节点最短路径为已确定,并将最短路径保存起来
//根据新确定最短路径的节点的邻接边,更新临时最短路径
ArcNode *q = a.vertices[index].firstarc;
while(q!=nullptr)
{
//若更小,则更新
if(lens[