这几次面试的确有好多次问到了最短路径算法,当时答得确实不好,对算法理解的不是很透彻,下面简单梳理一下。
Dijkstra算法有点DP的意思,适用于单源最短路径算法且要求边的权值非负,同时可以用于有向图和无向图。
map[][]
:存放原始图;dis[]
:存放从源点出发到达点i的最短路径长度;visited[]
:记录节点是否已被访问。dis[i] = map[0][i]
,从源点出发到达各点的距离,无法到达记为INT_MAX;visited(map.size(), 0), visited[0] = 1
,初始只访问过源点。双集合的说法可以查到很多,也解释的很详细,这里说一下我的理解。记源点为A。
i
的最短路径;visited[i] = 1
;i
中转到未访问节点的最短路径(假设i=B
),如A->B->C
,C为一未访问节点。这里的目前是指现在只能保证A->B
的路径最短,更新得到的A->B->C
路径只能保证现在是最短的。j
中转到达i
的路径比这一步找到的路径短,因为后面找到的可以从源点A到达的未访问点(假设目前找到了A->B
,存在B<->C,
):
A->C
,则有A->C->B
,如果A->C->B
的路径短于A->B
,则必有A->C
的路径短于A->B
的路径,那么这一步找到的最短路径便是A->C
,不会是A->B
;A->B->C
,假如有一条路径可以经过C和其他节点到达B,如A->B->C->D->B
,因为到达C首先已经经过了B,在经由C中转到达B的路径必包含A->B->C
,这种情况不可能发生。#include
#include
#include
using namespace std;
vector<int> Dijkstra(vector<vector<int>> &map);
int main(int argc, char *argv[]) {
const int M = INT_MAX;
vector<vector<int>> map
{
{0,6,3,M,M,M},
{6,0,2,5,M,M},
{3,2,0,3,4,M},
{M,5,3,0,2,3},
{M,M,4,2,0,5},
{M,M,M,3,5,0}
};
auto res = Dijkstra(map);
for (auto &ele : res)
cout << ele << " ";
cout << endl;
return 0;
}
vector<int> Dijkstra(vector<vector<int>> &map)
{
int size = map.size();
vector<int> dis(size, INT_MAX), visited(size, 0);
//初始化
for (int i = 0; i < size; i++)
dis[i] = map[0][i];
visited[0] = 1;
int t = size - 1;
while (t--)
{
int minval = INT_MAX, next;
//找到未访问节点中最小的路径,并记录该节点
for (int i = 1; i < size; i++)
{
if (!visited[i] && dis[i] < minval)
{
minval = dis[i];
next = i;
}
}
//更新visited
visited[next] = 1;
for (int i = 0; i < size; i++)
{
//对于节点next无法到达的节点,或者已经访问得到最短路径的节点,跳过
if (map[next][i] == INT_MAX || visited[i])
continue;
//检查中转路径是否比原路径短
dis[i] = min(dis[i], minval + map[next][i]);
}
}
return dis;
}
Prim算法适用于多源最短路径且不要求边的权值非负,同时可以用于有向图和无向图,但用于有向图的话要求两点之间来回的权值必须相同(待考证)。
dis[][]
代表从一点出发到达另一点的最短路径距离。从A到B的最短路径,要么有:
A>B
,代表从A直接到B;A->C->B
,代表从A经过任意次中转到B。代码就三层循环,很暴力,有两点要注意:
#include
#include
#include
using namespace std;
vector<vector<int>> Prim(vector<vector<int>> &map);
int main(int argc, char *argv[]) {
const int M = INT_MAX;
vector<vector<int>> map
{
{0,6,3,M,M,M},
{6,0,2,5,M,M},
{3,2,0,3,4,M},
{M,5,3,0,2,3},
{M,M,4,2,0,5},
{M,M,M,3,5,0}
};
auto res1 = Prim(map);
for (auto &a : res1)
{
for (auto &b : a)
cout << b << " ";
cout << endl;
}
return 0;
}
vector<vector<int>> Prim(vector<vector<int>> &map)
{
vector<vector<int>> dis(map.begin(), map.end());
int size = map.size();
for (int k = 0; k < size; k++)
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
//考虑溢出
if (dis[i][k] != INT_MAX && dis[k][j] != INT_MAX && dis[i][j] > dis[i][k] + dis[k][j])
dis[i][j] = dis[i][k] + dis[k][j];
return dis;
}
https://blog.csdn.net/ZHUO_SIR/article/details/80628663
https://blog.csdn.net/arthu6/article/details/80596772
https://www.cnblogs.com/hxsyl/p/3270401.html
https://cnblogs.com/ShiveryMoon/p/7859360.html