一、Dijkstra算法概述
Dijkstra算法是求从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止。
二、Dijkstra算法思想
当我们从起点开始搜寻时,如何才能够找到到达目标点的最短路径呢?我们刚开始只知道起点与他相邻的顶点,因此,我们只能够到达相邻顶点并标记到达所需要的最小代价。如图2.1所示,在已搜索的顶点中,到达各顶点的最短距离为直连距离。为了向更远的顶点进行搜索,我们采取广度优先搜索算法,找到1顶点连接的顶点,这些顶点可能是已经搜索过的2、4,也可能是没有搜索过的5、6;若经过1顶点到达2、4的距离小于原来的距离,那么更新到达2、4顶点的最短距离;由于5、6顶点从未搜索过,所以到达5、6的最短距离就是经过1顶点到达5、6顶点的距离,如图2.2所示。采取广度优先搜索依次搜索2、3、4顶点,以此类推,当遍历完所有的顶点时即能够找到从起点到整个图中其余顶点的最短距离。这个过程和我们探索的过程一样,我们已知一个地方A,然后向其他位置扩张,如果我们发现能够到达我们已知的地方B而且距离比原来已知的小,那么我们就记录下这个最短距离并尽量走这条路,如果找到一个新的地方C,那么我们只能够通过A去寻找C,其他的路并不能到达。这就是整个Dijkstra算法的思想,能够找到目前图中的最短路径。
三、Dijkstra算法实现
上述只是一个算法思想,当将思想转化为代码实现的时候会出现一定的问题,若我们采取邻接表形式存储图,则我们最终要找到最短路径,这时候我们需要几个数据结构:存储最短路径的数组dis[n],存储是否访问过的节点vis[n](上述原理采用广度优先,而邻接表形式则不是,后续进行说明),存储路径信息的pre[n]。见以下链接中的例子:https://blog.csdn.net/qq_35644234/article/details/60870719
当进行第一次搜索后(以起点访问),如下图所示:
此时图关系如下图所示:
此时我们选择离V1最近的顶点V3,这时候我们可以确定V1到V3的最短距离就是10(不会再有其他路径比这个更短了,那么我们就可以存储v3对应的pre为v1,即前置节点为v1,),这时候以V3为节点开始搜索,更新所有的dis(此时不按照上述例子进行讲述),即v2与v5的值可能发生变化。由于已经知道v3的最短距离,对以后的结果没什么影响,那么就可以将v3剔除出我们的图,即v3对应的vis为true,图简化为下图:
此后,我们依次简化寻找最短距离,将之踢出图,最终找到起点到所有节点的最短距离。每次找最短的距离都是为了更方便记录路径信息而已,否则随便寻找一个节点继续搜索只要搜索完所有节点也可以获得各最短距离,但是路径信息不好记录。
代码如下:
DJK.h
#pragma once
#define N 100
#define INF 10000
class DJK
{
public:
DJK();
~DJK();
int** MapCreat(int n);
void dijkstra(int **ayy,int v0, int n);
};
DJK.c
#include "stdafx.h"
#include "DJK.h"
#include<iostream>
using namespace std;
DJK::DJK()
{
}
DJK::~DJK()
{
}
int** DJK::MapCreat(int n)
{
if (n > N)
{
cout << "该图的节点过多,请扩大N!" << endl;
return NULL;
}
else
{
int i,j;
int **ayy;
ayy = (int**)malloc(n * sizeof(int));
cout << "请输入邻接矩阵,无穷大由-1表示!" << endl;
for (i = 0; i < n; i++)
{
ayy[i] = (int*)malloc(n * sizeof(int));
for (j = 0; j < n; j++)
{
cin >> ayy[i][j];
if (ayy[i][j] == -1)
ayy[i][j] = INF;
}
}
return ayy;
}
}
void DJK::dijkstra(int **ayy,int v0,int n)
{
int* distance;
bool *visit;
int i,j,k;
int mindis = INF+1;
int minnum = -1;
int *pre; //记录路径信息
int prev;
distance = (int*)malloc(n * sizeof(int));
visit = (bool*)malloc(n * sizeof(bool));
pre = (int*)malloc(n * sizeof(int));
for (i = 0; i < n; i++)
{
distance[i] = ayy[v0][i]; //初始化最短距离
visit[i] = false; //初始化未访问
if (ayy[v0][i] == INF)
{
pre[i] = -1;
}
else
pre[i] = v0;
}
distance[v0] = 0;
visit[v0] = true;
for (k = 1; k < n; k++)
{
mindis = INF + 1;
for (i = 0; i < n; i++)
{
if (distance[i] < mindis && visit[i] == false)
{
mindis = distance[i];
minnum = i;
}
}
visit[minnum] = true;
for (j = 0; j < n; j++)
{
if (visit[j] == false && ayy[minnum][j] <INF)
{
if (distance[minnum] + ayy[minnum][j] < distance[j])
{
distance[j] = distance[minnum] + ayy[minnum][j];
pre[j] = minnum;
}
}
}
}
for (i = 0; i < n; i++)
{
if (pre[i] == -1)
{
cout << "到" << i << "节点没有通路!" << endl;
continue;
}
cout << "到" << i << "节点最短的路径为:" << endl;
if (pre[i] != -1)
{
cout << i << " ";
prev = pre[i];
while (prev != v0)
{
cout << prev << " ";
prev = pre[prev];
}
cout << v0 << endl;
}
cout << endl << endl;
cout << "到" << i << "节点最短的长度为:" << distance[i]<<endl;
cout << endl << endl;
}
}
main.c
// Dijkstra.cpp : 定义控制台应用程序的入口点。
//
#include "stdafx.h"
#include "DJK.h"
#include<iostream>
using namespace std;
int main()
{
DJK Mydjk;
int n;
int ch = 1;
int v0;
int **ayy;
int i, j;
cout << "请输入图节点数目!" << endl;
cin >> n;
ayy = Mydjk.MapCreat(n);
cout << "您输入的邻接矩阵为:" << endl;
for (i = 0; i < n; i++)
{
for (j = 0; j < n; j++)
{
cout << ayy[i][j]<<" ";
}
cout << endl;
}
while (ch)
{
cout << "请输入要起头的节点:" << endl;
cin >> v0;
Mydjk.dijkstra(ayy, v0, n);
cout << endl << endl << endl;
cout << "输入0结束,输入1继续查找其他节点!" << endl;
cin >> ch;
cout << endl << endl << endl;
}
return 0;
}