这里写自定义目录标题
图论问题一般分为:
- 单源最短路径问题:从某固定源点出发,求其到所有其他顶点的最短路径
- (有向)无权图
- (有向)有权图
- 多源最短路径问题:求任意两顶点间的最短路径
1.迪杰斯特拉Dijkstra
1.1原理详解
有权图的单源最短路径算法——dijkstra;
不能处理带负边权的情况,用邻接矩阵或邻接表存图
——————————————————————————
所以 最短路径,无向图,正边权——Dijkstra算法
———————————————————————————
Dijkstra进行进一步的堆优化以后时间复杂度成为O(nlogn),
Floyd的O(n^3)
Dijkstra,以及常用的还有Bellman-Ford,SPFA等均是在求单源最短路径的问题中有着较为理想的时间复杂度(<=O(n^2)),
但若是求图中任意两点间的距离,尤其是在图较为稠密时,Floyd的O(n^3)也是不输于其他的。
————————————————————————————————————
实现思路——个人理解:
函数原型 Dijkstra(int s)
-
visited数组,distance数组,pre前驱点数据||初始化,visited全部未访问,distance数组全部最大;distance表示各个其他点距离s的距离;pre表示当前点前一个点,初始化为-1(和dijkstra原理有关,原理移步B站)
-
distance初始化,dis[s] = 0;
-
根据邻接矩阵和邻接表;遍历所有点,对与s相连通的点的dis[index]进行初始化,dis[index] = map[s][index] (邻接矩阵);其中图中与s不连通的点,其dis值仍为inf;同时更新上述连通的前驱点
pre[index] = s;
-
上面是初始化操作,下面是建dijikstra表操作
-
遍历所有点,for(int index = 0;index<N;index++) 。找出未访问过的且距离起始点s最近的一个点;(就拿上面的图来说,倘若起始点s=A,此时遍历所有点,会找到D点,因为D点距离A最近)。保存该点的dis值和索引值,将该点的dis报错为distemp,索引值保存为indextemp;最重要的要标记该点已经访问过了;visited[index] = true; ————————访问数组visit第五步用到
-
再次遍历所有点;目的是为了对已建好的表进行优化;
-
遍历所有点,for(int index = 0;index<N;index++) 。在未访问过的店中,检查dis[index]与dis[indextemp]+ map[index] [indextemp] 的值的大小。如果后者更小的话,就需要对图进行优化。更新距离值和前驱点:————————访问数组visit第六步也用到
dis[index] = dis[indextemp]+ map[index] [indextemp];
pre[index] = indextemp;
-
针对结果输出的话
- s点到i点的距离:直接输出dis[i]的值就行 - s点到i点的路径:
//目标点的前驱点为 p[i]
// p[i]点的前驱点 p[p[i]] ……
void prin(int i,int s)
{
//倒序输出
cout<<p[i]<<" "
if(p[i] != s)
prin(p[i],s);
}
1.2例题应用
以上面那张图为例ABCDE
#include <iostream>
#include <string>
#include <sstream>
#include <cstring>
using namespace std;
#define N 5
#define INF 0x3f3f3f
int dis[N];
bool visit[N];
int pre[N];
// void Dijkstra(int s) //建表过程 ,建立图中所有点与s的最短路径
int Dijkstra(int s,int e,int map[N][N])//s->e的最短路径
{
memset(visit, false, sizeof(visit));
memset(dis, INF, sizeof(dis));
memset(pre, -1, sizeof(pre));
dis[s] = 0;
for (int i = 0; i < N; i++)
{
dis[i] = map[s][i];
pre[i] = s;
}
//
//建表操作
for (int i = 0; i < N; i++)
{
int distemp = INF;
int indextemp;
//找一个最近点 遍历所有点
for (int j = 0; j < N; j++)
{
if ( !visit[j] && dis[j] < distemp)
{
distemp = dis[j];
indextemp = j;
}
}
visit[indextemp] = true; //标记访问过
// 第二次遍历 目的是优化
for (int j = 0; j < N; j++)
{
if (!visit[j] && dis[j] > map[indextemp][j] + distemp) //言外一直 a->d的直接距离 大于 a->index->d
{
dis[j] = map[indextemp][j] + distemp;
pre[j] = indextemp;//前驱点更新
}
}
}
//所有遍历完,表就建好了,有用的主要是前驱表pre[]和距离表dis[]
return dis[e];
}
void prin(int i,int s)
{
cout << pre[i] << "<-";
if(pre[i]!=s)
prin(pre[i],s);
}
int main()
{
int map[5][5] =
{
{0, 6, INF, 1, INF}, //不直接相连的点距离就是无穷大
{6, 0, 5, 2, 2},
{INF, 5, 0, 5, INF},
{1, 2, INF, 0, 1},
{INF, 2, 5, 1, 0},
};
int res = Dijkstra(0,2,map);
cout << res << endl;
cout << "2";
prin(2,0);
return 0;
}