算法介绍
这个算法我们生活中会经常使用到,比如我们到了一个新的城市。经常会使用高德地图导航来找到距离目的地的最短路径,地图会给出我们具体的行走路线,大约需要花费的时间。一般会给出最短时间和最短路径两种方案,起始算法是一样的。只需要将路径的权值换为平均时间就可以。
这里我们讨论的是需要到多个目的地,求出分别到每个目的地的最短距离。比如我们今天想去“颐和园”,明天想去“长城”。这是一个求单元最短路径的问题。给定有向带权图 G = ( V , E ) G = (V,E) G=(V,E),其中每条边的权是非负实数。此外,给定V中的一个顶点,称为源点。这里的路径长度指路上各边的权之和。
强烈推荐小白下载这个APP,快速搞懂算法运行流程。
算法动态图解:链接:https://pan.baidu.com/s/1mX3s7VjLTKLr7MZhAQO-6Q
提取码:cv5y
算法流程
- 1 数据结构,设置地图的带权邻接矩阵为 m a p [ ] [ ] map[][] map[][],即如果从源点 u u u到顶点 i i i有边,令 m a p [ u ] [ i ] map[u][i] map[u][i]=两个点之间的距离,否则将 m a p [ u ] [ i ] map[u][i] map[u][i]= ∞ \infty ∞;采用一维数组 d i s t [ ] i dist[]i dist[]i记录从顶点 i i i到源点的距离;采用一维数组 p [ ] p[] p[]来记录最短距离 i i i顶点的前驱。
- 2 初始化。令集合 S = { u } S=\{u\} S={ u},对于集合中 V − S V-S V−S中的所有顶点 x x x,初始化 d i s t [ i ] = m a p [ u ] [ i ] dist[i]=map[u][i] dist[i]=map[u][i],初始化前驱数组 p p p,如果顶点 i i i到源点 u u u有边相连,初始化 p [ i ] = u p[i]=u p[i]=u,否则 p [ i ] = − 1 p[i]=-1 p[i]=−1;
- 3 找最小。在集合 V − S V-S V−S中依照贪心策略来U型你找是的 d i s t [ i ] dist[i] dist[i]最小的顶点 t t t,即 d i s t [ i ] dist[i] dist[i] = m i n ( d i s t [ j ] ) min(dist[j]) min(dist[j]), d i s t [ j ] dist[j] dist[j]属于 V − S V-S V−S集合,则顶点 t t t就是集合 V − S V-S V−S中距离源点 u u u最近的顶点。
- 4 加入 S S S集合。将顶点 t t t加入集合 S S S中,同事更新 V − S V-S V−S。
- 5.判断是否结束。如果 V − S V-S V−S集合为空,则算法结束,否则进行第6步
- 6 借东风。在步骤3中已经找到了源点到 t t t的最短路径,那么对于集合 V − S V-S V−S中所有与顶点 t t t相邻的顶点 j j j,都可以借助 t t t走捷径。前提是捷径的距离更短,即 d i s t [ j ] > d i s t [ t ] + m a p [ t ] [ j ] dist[j]>dist[t]+map[t][j] dist[j]>dist[t]+map[t][j],则 d i s t [ j ] = d i s t [ t ] + m a p [ t ] [ j ] dist[j]=dist[t]+map[t][j] dist[j]=dist[t]+map[t][j],记录顶点 j j j的钱去为 t t t。有 p [ j ] = t p[j]=t p[j]=t。然后继续步骤3。
源代码
#include<iostream>
#include<windows.h>
#include<stack>
using namespace std;
const int N = 100;
const int INF = 1e7;
int map[N][N], dist[N], p[N], n, m;
bool flag[N];
void Dijkstra(int u)
{
//2.初始化,前驱数组,距离和集合
for (size_t i = 1; i <= n; i++)
{
dist[i] = map[u][i];//初始化源点到其他点的距离
flag[i] = false;
if (dist[i] == INF)//如果源点到其他点存在路径则将前驱设置为源点u
{
p[i] = -1;
}
else
{
p[i] = u;
}
}
dist[u] = 0;
flag[u] = true;//初始化S集合有一个元素:源点u
//3.求最小,在集合V-S中找到距离源点u最近的顶点t,如果找到则将t加入到集合S中。否则跳出循环
for (size_t i = 1; i <= n; i++)//初始化V-S集合有n-1个元素
{
int temp = INF, t = u;
for (size_t j = 1; j <= n; j++)
{
if (!flag[j] && dist[j] < temp)
{
t = j;
temp = dist[j];//在V-S集合中找到最短距离
}
}
if (t == u) {
return; }
flag[t] = true;
//更新V-S集合中与t邻接的顶点
for (size_t j = 1; j <= n; j++)
if (!flag[j] && map[t][j] < INF)
//如果经过t点,路径更短。更新距离和前驱
if (dist[j] > (dist[t] + map[t][j]))
{
//4.借东风,在已有的t顶点,如果源点经过t到达邻接点j的路径更短。更新矩阵和j的前驱为t
dist[j] = dist[t] + map[t][j];
p[j] = t;
}
}
}
int main()
{
int u, v, w, st;
system("color 0d");
cout << "请输入城市的个数:" << endl; cin >> n;
cout << "请输入城市之间的路线的个数:" << endl; cin >> m;
cout << "请输入城市之间的路线以及距离:" << endl;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
{
map[i][j] = INF;//初始化邻接矩阵为无穷大
}
while (m--)
{
cin >> u >> v >> w;
map[u][v] = min(map[u][v], w); //邻接矩阵储存,保留最小的距离
}
cout << "请输入小明所在的位置:" << endl; ;
cin >> st;
Dijkstra(st);
cout << "小明所在的位置:" << st << endl;
for (int i = 1; i <= n; i++)
{
cout << "小明:" << st << " - " << "要去的位置:" << i;
if (dist[i] == INF)
cout << "sorry,无路可达" << endl;
else
cout << " 最短距离为:" << dist[i] << endl;
}
//findpath(st);
return 0;
}
时间复杂度
在 D i j k s t r a Dijkstra Dijkstra算法中,一共有四个for语句,第一个for语句执行n次。第二个for语句中嵌套了两个for语句,其中嵌套的两个for语句为并行关系(各执行n次)。总的执行次数为 2 n ∗ n + n 2n*n+n 2n∗n+n,时间复杂度为 O ( n 2 ) O(n^2) O(n2)。