图结构专栏——Dijkstra算法详解(大量注释)
Dijkstra 算法是求一个图中一个点到其他所有点的最短路径的算法
一、算法详解
每次从 「未求出最短路径的点」中 取出 距离距离起点 最小路径的点,以这个点为桥梁 刷新「未求出最短路径的点」的距离,可以结合下面的案例来理解。
对于这样一个无向图,我们希望求得节点3到其他节点的最短路径长度。
首先,我们定义一个数组distance记录了节点3到其他节点的最短路径长度,再定义一个数组visited记录哪些节点到3节点的最短路径长度已经计算过了,这些数组初始化都为-1。
1、首先将我们的目标节点3加入visited中,同时更新distance表
这次结束后visited表:
1 | 2 | 3 | 4 |
---|---|---|---|
-1 | -1 | 0 | -1 |
因为节点3被加入到visited表,所以更新distance表,更新的方法是使用节点3到其他节点的路径长度
1 | 2 | 3 | 4 |
---|---|---|---|
6 | -1 | -1 | 2 |
我们需要将distance表中长度最小的而且不在visited数组中的节点取出来,即为4节点
2、这一步将我们上一步取得的节点4加入visited中,同时更新distance表
这次结束后visited表:
1 | 2 | 3 | 4 |
---|---|---|---|
-1 | -1 | 0 | 0 |
因为节点4被加入到visited表,所以更新distance表,之后更新distance表的方法是
- 对于每个节点j,如果他不在visited数组,并且他与此刻加入visited数组的节点i相连,那么
- 如果distance[j] = -1,说明之前加入的节点与该节点j不直接连通,那么此时distance[j]的值就是节点i,j路径的权值
- 如果distance[j] >=0,说明之前加入的节点与该节点j直接连通,那么要判断这一步加入了节点i会不会产生目标节点3到节点j更短的路径。那么distance[j] = min( distance[j] , distance[i] + graph[i][j] )
举例来说,加入了节点4以后,因为节点2与节点4相连,因此distance[2]就等于distance[4] + graph[4][2] = 2 + 3 = 5
加入了节点4以后,判断节点1的情况:虽然第一步中得到distance[1] = 6,但是由于加入了新节点4,而节点4与节点1相连,那么distance[j] = min( distance[j] , distance[i] + graph[i][j] ) = min(6 , 2 + 2) = 4
那么更新后的distance表即为
1 | 2 | 3 | 4 |
---|---|---|---|
4 | 5 | -1 | 2 |
我们需要将distance表中长度最小的而且不在visited数组中的节点取出来,即为1节点
3、这一步将我们上一步取得的节点1加入visited中,同时更新distance表
这次结束后visited表:
1 | 2 | 3 | 4 |
---|---|---|---|
0 | -1 | 0 | 0 |
更新后的distance表即为
1 | 2 | 3 | 4 |
---|---|---|---|
4 | 5 | -1 | 2 |
我们需要将distance表中长度最小的而且不在visited数组中的节点取出来,即为2节点
4、这一步将我们上一步取得的节点2加入visited中,同时更新distance表
这次结束后visited表:
1 | 2 | 3 | 4 |
---|---|---|---|
0 | -1 | 0 | 0 |
更新后的distance表即为
1 | 2 | 3 | 4 |
---|---|---|---|
4 | 5 | -1 | 2 |
至此我们将所有的节点都放入visited表中,表示求解结束。
二、代码
vector<int> visited;//记录哪些节点的结果被求出来,初始都是0,当一个节点的结果被求出来后值变为-1
vector<vector<int>> graph;//记录图的信息,不连通的边为-1
vector<int> distance;//记录结果,即为目标节点target到其他节点的最短距离,特别的distance[target] = 0,初始值是-1
//weight存储节点之间的权重 eg:[[2,1,1],[2,3,1],[3,4,1]],第一个元素意思是2节点指向1节点,而且这条边的权值为1(节点从0开始)
//n是节点数
//k是要判断第k个节点(节点从0开始)
vector<int> networkDelayTime(vector<vector<int>>& weight, int n, int k) {
graph.resize(n + 1, vector<int>(n + 1, -1));
distance.resize(n + 1, -1);
distance[k] = 0;
visited.resize(n + 1, 0);
for (int i = 0; i < weight.size(); i++)
{
int one = weight[i][0];//有向边的尾部
int two = weight[i][1];//有向边的头部,即one->two
int thisweight = weight[i][2];//这条有向边的权值
graph[one][two] = thisweight;//这条边的信息加入图矩阵中
}
for (int i = 0; i < graph[k].size(); i++)
{
distance[i] = graph[k][i];//第一次将目标节点到其他节点的长度记录在distance中
}
visited[k] = - 1;
for (int i = 1; i <= n; i++)
{
int mindis = (1 << 31) - 1;
int minindex = 0;
//遍历节点,找到不在visited数组同时在distance中值最小的节点
for (int j = 1; j <= n ; j++)
{
if (visited[j] == 0 && distance[j] < mindis && distance[j] >= 0)
{
mindis = distance[j];
minindex = j;
}
}
//得到该节点后加入visited数组
visited[minindex] = -1;
//遍历与该节点相连的不在visited数组的节点,判断这些节点的distance需不需要被更新
for (int j = 1; j <= n; j++)
{
if (graph[minindex][j] >= 0 && visited[j] == 0)
{
int thisdistance = distance[minindex] + graph[minindex][j];
if (distance[j] > thisdistance || distance[j] == -1)
{
distance[j] = thisdistance;
}
}
}
}
return distance;
}