迪杰斯特拉算法是很经典的最短路径算法,计算方式是从源点开始向外扩散,依次选点,直到点所有点选完之后,该源点到所有点的最短路径则计算完成。由于这个算法遍历了所有点,并且不断的在更新距离,用于选点,所以效率较低。
要进行计算距离,首先要准备一个重要的矩阵数据,所有点连通性即距离,所有计算都是基于这个矩阵来进行计算的,下面开始讲解迪杰斯特拉的具体计算过程:
以这个图为例,开始讲解:
以v0为源点,计算v0到各个顶点的最短距离,假设有两个集合:S,V,S集合是存放已经选择完成的点,V集合存放未计算完成的点,假设dist标记,如果某段路径x计算完成,就将dist[x] = 1(dist数组初始化的时候只有源点是1,其余都是-1),S集合最初只有源点V0,V集合中有除源点之外的所有点。
1、V0只能到V1和V2两个点,且V0-V1近,则将V1加入S集合,更新最新路径:S加入V1后,可以到达V2,V3,V4三个点,距离分别为4,8,6。
2、将V2加入S集合,然后再更新最新距离,可达到V3,V4,V5,距离分别为,8,6,11,
依次进行,知道所有点加入S集合,在加点的同时,也在不断的更新dist数组,把相应的位置设置为1.并且还要更新一个前驱点集合,这是集合是将来用于寻找最短路径的关键信息。理论基本如此,以下是代码实现:
@interface PathNodeData : NSObject
{
@public
SPathNode *pathList;//过程路径
NSInteger pathListNum;
int nodeNumber;//节点数量
SPathNode *points;//节点集合
int distanceNum;//路径联通信息数量
SMatrixNode *distances;//节点联通状态集合
float *edgeMatrix;//节点距离矩阵
}
//以距离连接点矩阵初始化
- (instancetype)initWithMatrixData:(SPathAMatrix)pAndm;
/**
* 计算路径
*
* @param startIdx 起点
* @param endIdx 终点
*/
- (void)dijkstra:(uint64_t)startIdx andEnd:(uint64_t)endIdx;
#import "PathNodeData.h"
#define INF 9999.f
@implementation PathNodeData
- (instancetype)initWithMatrixData:(SPathAMatrix)pAndm{
self = [super init];
if (self) {
nodeNumber = pAndm.pathNum;
points = pAndm.paths;
distanceNum = pAndm.matrixNum;
distances = pAndm.matrixs;
[self initData];
}
return self;
}
- (void)initData{
edgeMatrix = malloc(sizeof(float)*nodeNumber*nodeNumber);
for (int i = 0; i < nodeNumber; i++) {
for (int j = 0; j < nodeNumber; j++) {
edgeMatrix[i*nodeNumber+j] = INF;
}
}
for (int i =0 ; i<distanceNum; i++) {
SMatrixNode dist = distances[i];
edgeMatrix[dist.row*nodeNumber+dist.column] = dist.value;
edgeMatrix[dist.column*nodeNumber+dist.row] = dist.value;
}
}
- (void)dijkstra:(uint64_t)startIdx andEnd:(uint64_t)endIdx {
int startIndex = 0,endIndex = 0;
for (int i = 0; i<nodeNumber; i++) {
if (points[i].idd == startIdx) {
startIndex = i;
}
if (points[i].idd == endIdx) {
endIndex = i;
}
}
int v,w,k = -1,min;
int *final = malloc(sizeof(int)*nodeNumber); // final[i]=1表示"顶点vs"到"顶点i"的最短路径已成功获取。
int *pathMatrix = malloc(sizeof(int)*nodeNumber);//储存最短路径下标
int *shortPathTabel = malloc(sizeof(int)*nodeNumber);//用于储存各点路径权值和
// 初始化
for (v = 0; v < nodeNumber; v++)
{
final[v] = 0; // 顶点v的最短路径还没获取到。
pathMatrix[v] = -1; // 初始化路径数组为0。
shortPathTabel[v] = edgeMatrix[startIndex*nodeNumber+v];// 顶点i的最短路径为"顶点vs"到"顶点i"的权。
}
// 对"顶点vs"自身进行初始化
shortPathTabel[startIndex] = 0;
final[startIndex] = 1;
// 遍历nodeNumber-1次;每次找出一个顶点的最短路径。
for (v = 1; v < nodeNumber; v++){
// 寻找当前最小的路径;
// 即,在未获取最短路径的顶点中,找到离vs最近的顶点(k)。
min = INF;
for (w = 0; w < nodeNumber; w++)
{
if (!final[w] && shortPathTabel[w]<min)
{
min = shortPathTabel[w];
k = w;
}
}
// 标记"顶点k"为已经获取到最短路径
final[k] = 1;
// 修正当前最短路径和前驱顶点
// 即,当已经"顶点k的最短路径"之后,更新"未获取最短路径的顶点的最短路径和前驱顶点"。
for (w = 0; w < nodeNumber; w++)
{
if (!final[w] && (min+edgeMatrix[k*nodeNumber+w] < shortPathTabel[w]))
{
shortPathTabel[w] = min+edgeMatrix[k*nodeNumber+w];
pathMatrix[w] = k;
}
}
//指定终点路径算完,终止循环,不终止的话会把所有点到源点路径计算完。
if (k == endIndex) {
break;
}
}
[self getShortMax:pathMatrix Start:startIndex End:endIndex];
free(final);
free(pathMatrix);
free(shortPathTabel);
final = nil;
pathMatrix = nil;
shortPathTabel = nil;
}
- (void)getShortMax:(int *)p Start:(int)strIdx End:(int)endIdx{
NSMutableArray *pathArray = [NSMutableArray array];
int whileTag = endIdx;
[pathArray addObject:@(endIdx)];
while (YES) {
whileTag = p[whileTag];
if (whileTag < 0) {
[pathArray addObject:@(strIdx)];
break;
}
[pathArray addObject:@(whileTag)];
}
if (pathList!=nil) {
free(pathList);
pathList = nil;
}
pathList = malloc(sizeof(SPathNode)*pathArray.count);
for (int i =0 ; i<pathArray.count; i++) {
SPathNode pn = points[[pathArray[i] intValue]];
pathList[pathArray.count-1-i] = pn;
}
pathListNum = pathArray.count;
}
- (void)dealloc{
free(points);
free(distances);
free(pathList);
free(edgeMatrix);
points = nil;
distances = nil;
pathList = nil;
edgeMatrix = nil;
}
代码中各部分注释很明确,下面对于读取最短路径作一个简短的说明,pathMatrix这个数组中存放着最短路径下标,但是直接读取是得不到的,首先将传进来的目标点,也就是终点作为数组下标,p[whileTag]得到一个下一个下标,whileTag = p[whileTag] ,然后同上,用现在的whileTag去获取p[whileTag],直到p[whileTag]的值为负(因为我初始化P数组的时候默认都为-1),则终止循环,过程中这些whileTag是需要保存的,他们按顺序排下来就是终点到起点的最短路径。
如果,你在程序中没有使用
//指定终点路径算完,终止循环,不终止的话会把所有点到源点路径计算完。
if (k == endIndex) {
break;
}
则程序会自动计算源点到所有点最短路径,则pathMatrix也不会存为负的情况(除非某个点是独立的,没有路径通向它),这样吧终点作为whileTag循环取下标,直到取得的p[whileTag]的值是你的起点,也要终止循环,整理下标。
以上则是迪杰斯特拉算法,初看可能不会立刻明白,结合程序以及LOG一些数组信息,则会慢慢理解......