Dijkstra是一种求单源点最短路径的算法,即求某一个顶点到其余各顶点之间的最短路径,下面采用邻接矩阵来存储图中信息
算法思路为:
1,假设v0是源点,s是已求得最短路径的终点集合,用D[i]来保存从v0到vi顶点之间的最短路径。初始:将v0置于集合s之中,
2,初始化D[i]为v0到vi之间的权值。a,求从v0出发长度最短的路径,通过计算D[j]=min{D[i]|vi不属于s},找到j顶点,将j顶点加入s集合。b,求下一条最短路径,因为有新的结点加入到s集合中,所以需要修改从v0到其余不属于s的顶点之间的D[i]上的值。即若D[j]+arcs[j][k]<D[k](k不属于s中),修改D[k]值为D[j]+arcs[j][k]
3,重复2中的a,b步骤
示例如下
按照上面思路列出每一次循环的结果列表如下:
第一次 | 第二次 | 第三次 | 第四次 | 第五次 | |
---|---|---|---|---|---|
v1 | ∞ | ∞ | ∞ | ∞ | ∞ |
v2 | 10 (v0,v2) | \ | \ | \ | \ |
v3 | ∞ | 60 (v0,v2,v3) | 50 (v0,v4,v3) | \ | \ |
v4 | 30 (v0,v4) | 30 (v0,v4) | \ | \ | \ |
v5 | 100 (vo,v5) | 100 (vo,v5) | 90 (v0,v4,v5) | 60 (vo,v4,v3,v5 | \ |
vj | v2 | v4 | v3 | v5 | \ |
s | {vo,v2} | {vo,v2,v4} | {vo,v2,v4,v3} | {vo,v2,v4,v3,v5} | ∞ |
辅助结构
bool final[maxSize];//表示集合s
final[i] = true;//顶点i在s中
final[i] = false;//i不在s中
bool p[maxSize][maxSize];//表示路径,
p[v][w] = true;//表示w顶点属于从源点到v顶点最短路径上的一个顶点
p[v][w] = false;//表示w顶点不属于从源点到v顶点最短路径上的一个顶点
int D[maxSize];//表示从源点到各个顶点之间最短路径长度
Dijkstra算法
void dijkstra(mGraph& G, int v0)
{
int i, j, minValue, min;
//初始化
for (i = 0; i < G.vexnum; ++i)
{
final[i] = false;
D[i] = G.arc[v0][i];
for (j = 0; j < G.vexnum; ++j)
p[v0][j] = false;
if (G.arc[v0][i] < infini)
{
p[i][v0] = true;
p[i][i] = true;
}
}
final[v0] = true;//v0入集合
D[v0] = 0;
for (i = 1; i < G.vexnum; ++i)//循环G.vexnum-1次
{
//找到最小的D[i]
minValue = infini;
for (j = 0; j < G.vexnum; ++j)
{
if (final[j]==false)
if (minValue>D[j])
{
minValue = D[j];
min = j;
}
}
final[min] = true;
//更新
for (j = 0; j < G.vexnum; ++j)
{
if (final[j] == false && minValue + G.arc[min][j] < D[j])
//若G.arc[min][j]为INT_MAX,左边相加则会溢出,所以不能使用INT_MAX
{
//更新D
D[j] = minValue + G.arc[min][j];
//更新p
for (int temp = 0; temp < G.vexnum; ++temp)
p[j][temp] = p[min][temp];
p[j][j] = true;
}
}
}
}
完整示例
#include<iostream>
#define infini INT_MAX/2//最大值不选取INT_MAX,防止溢出
#define maxSize 6//顶点数目
#define MAX 8//边的个数
//邻接矩阵
typedef struct
{
int vexnum, arcnum;//顶点数和边数
char vex[maxSize];//顶点信息(字符)
int arc[maxSize][maxSize];//二维数组(存储边上的信息)
}mGraph;
bool final[maxSize];//表示集合s
bool p[maxSize][maxSize];//表示路径
int D[maxSize];//表示从源点到各个顶点之间最短路径长度
void dijkstra(mGraph& G, int v0);//求最短路径
int main()
{
using namespace std;
mGraph G;//邻接矩阵存储图并进行初始化
G.vexnum = maxSize;
G.arcnum = MAX;
char vexData[maxSize] = { 'a', 'b', 'c', 'd', 'e','f' };//顶点信息
int arcData[MAX][3] = { { 0, 2 ,10}, { 0, 5,100 }, { 0, 4 ,30}, { 1, 2,5 }, { 2, 3 ,50},
{ 3, 5, 10 }, { 4, 3, 20 }, { 4, 5, 60 } };//连接的边
int i, j;
for (i = 0; i < G.vexnum; ++i)
{
G.vex[i] = vexData[i];
for (j = 0; j < G.vexnum; j++)
G.arc[i][j] = infini;
}
for (i = 0; i < G.arcnum; i++)
G.arc[arcData[i][0]][arcData[i][1]] = arcData[i][2];
//初始化完毕
cout << "求从v0顶点出发的最短路径: ";
cout << endl;
dijkstra(G, 0);
for (i = 1; i < G.vexnum; ++i)
{
if (D[i] == infini)
{
cout << "不存在到v" << i
<< "的路径" << endl;
continue;
}
cout << "到v" << i << ": 长度为" << D[i] << ' '
<< "最短路径上的顶点为";
for (j = 0; j < G.vexnum; ++j)
if (p[i][j] == true)
cout << 'v' << j << ' ';
cout << endl;
}
cout << endl;
return 0;
}
void dijkstra(mGraph& G, int v0)
{
int i, j, minValue, min;
//初始化
for (i = 0; i < G.vexnum; ++i)
{
final[i] = false;
D[i] = G.arc[v0][i];
for (j = 0; j < G.vexnum; ++j)
p[v0][j] = false;
if (G.arc[v0][i] < infini)
{
p[i][v0] = true;
p[i][i] = true;
}
}
final[v0] = true;//v0入集合
D[v0] = 0;
for (i = 1; i < G.vexnum; ++i)//循环G.vexnum-1次
{
//找到最小的D[i]
minValue = infini;
for (j = 0; j < G.vexnum; ++j)
{
if (final[j]==false)
if (minValue>D[j])
{
minValue = D[j];
min = j;
}
}
final[min] = true;
//更新
for (j = 0; j < G.vexnum; ++j)
{
if (final[j] == false && minValue + G.arc[min][j] < D[j])
//若G.arc[min][j]为INT_MAX,左边相加则会溢出,所以不能使用INT_MAX
{
//更新D
D[j] = minValue + G.arc[min][j];
//更新p
for (int temp = 0; temp < G.vexnum; ++temp)
p[j][temp] = p[min][temp];
p[j][j] = true;
}
}
}
}
输出结果为下图:
Floyd算法求最短路径:
https://blog.csdn.net/Little_ant_/article/details/104293812
附:如果采用上面链接博文中的Floyd算法求上图的最短路径,结果如下:
补充Java代码实现
细节后面在阐述,代码如下:
public class Solution {
static class State{
int id; //图节点的 id
int distFromStart;
State(int i,int d){
id=i;
distFromStart=d;
}
}
int[] d(int start,List<Integer>[] graph){
int vs=graph.length; // 图中节点的个数
int[] distTo=new int[vs];// distTo[i]:表示从 start 到 i 的最短路径权重
Arrays.fill(distTo, Integer.MAX_VALUE);
distTo[start]=0;
Queue<State> pq=new PriorityQueue<>((State a,State b)->{
return a.distFromStart-b.distFromStart;
}); // distFromStart 小的排在前面
pq.offer(new State(start,0));
while (!pq.isEmpty()){
State curState=pq.poll();
int curNodeID=curState.id,curDistFromStart=curState.distFromStart;
if(curDistFromStart>distTo[curNodeID]) //因为我们会重复添加同一个节点,但是它们的权重是不一样的,权重大的直接被舍弃
continue;
for(int nextNodeID:graph[curNodeID]){
int distToNextNode=distTo[curNodeID]+weight(curNodeID,nextNodeID);
if(distToNextNode<distTo[nextNodeID]){
distTo[nextNodeID]=distToNextNode;
pq.offer(new State(nextNodeID,distToNextNode));
}
}
}
return distTo;
}
int weight(int from,int to){
return 0;//根据具体情况来实现。
}
}