迪杰斯特拉(dijkstra)
单源正权问题求最短路的算法
算法核心思想:
- 初始化所有点到源的距离为无穷大的数,为了方便接下来的比较。
- 每次找到距离 源(起点) 最近的点(当前最小点),用该点更新其余每个点到源(起点)的距离。
- 更新距离的思想:判断该点直接到源的距离,和经过当前最小点到源的距离进行比较,如果小,就更新,依次更新遍历每个点。
- 循环遍历出所有的当前最小点。
dijkstra的两种实现:
- 朴素版dijkstra(时间复杂度:,n为图上的所有点)
- 堆优化版的dijkstra(时间复杂度:,n为图上的所有点,m为边的总数
比较两种时间复杂度,发现:
- 朴素版的时间复杂度与图的边数无关,因此,更适于稠密图
- 堆优化版的时间复杂度与当n与m近似相等的时候,此时时间复杂度为,优于朴素版,因此,更适于稀疏图
前缀知识:
稠密图:
图的边数比较多,而点数比较少,用邻接矩阵进行存储数据
邻接矩阵:
通俗为 二维数组 g[N][N]
- 当边有权重的时候,则表示权重
- 当边无权重的时候,则表示两点是否相连
稀疏图:
图的点数比较多,而边数比较少,用邻接表进行存储
邻接表:
可以理解为数组实现的链表,区别,多个头节点,h[N]
加边方式:
int h[N],e[N],ne[N],idx,w[N]; //h为头节点,e为当前的点,ne为对下一个点的索引,w为权重
memset(h,-1,sizeof h) //初始化h,保证h最后一个节点指向-1
void add(int a,int b,int c) //a为出发点,b为指向的点,c为这条边权重
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
}
如果为双向边,则add(a,b,c),add(b,a,c)
朴素版实现:
#include<bits/stdc++.h>
using namespace std;
const int N=550;
int g[N][N]; //邻接矩阵
int dis[N]; //表示到源的距离
int n,m;
bool is_used[N]; //判断当前点是否已经是最小距离点
int dijkstra()
{
dis[1]=0; //初始1的点的距离为0
for(int i=0;i<n;i++) //循环每个点,使得每个点都是最小距离点
{
int t=-1; //标记未选中当前点
for(int j=1;j<=n;j++) //找到当前最小距离的点
{
if(!is_used[j]&&(t==-1||dis[j]<dis[t])) t=j;
}
is_used[t]=true; //把当前最小距离的点标记为最小距离的点
for(int j=1;j<=n;j++) //循环更新所有点的距离
dis[j]=min(dis[j],dis[t]+g[t][j]);
}
if(dis[n]==0x3f3f3f3f) return -1; //距离未更新,则不能到达
else return dis[n];
}
int main()
{
memset(g,0x3f,sizeof g);
memset(dis,0x3f,sizeof dis);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
g[a][b]=min(g[a][b],c); //取最小的重边
}
cout<<dijkstra()<<endl;
return 0;
}
堆优化版实现:
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5+10;
int h[N],e[N],ne[N],w[N],idx; //邻接表的构成
int n,m;
int dis[N]; //点到源的距离
bool is_used[N]; //判断点是否已经是最小距离点
typedef pair<int,int> PII; //first存距离,根据距离进行堆排序;second表示点号
priority_queue<PII,vector<PII>,greater<PII>> heap; //优先队列的定义
void add(int a,int b,int c)
{
e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++; //邻接表的连接方式
}
int dijkstraHeap()
{
dis[1]=0;
heap.push({0,1}); //将1号点放入
while(heap.size())
{
auto t=heap.top(); //去除最小距离的点,确定为当前最小距离点
heap.pop(); //排出该点,去掉冗余
int pos=t.second,distance=t.first;
if(!is_used[pos]) //如果这个点不是冗余,表示还不是最小距离点
{
for(int i=h[pos];i!=-1;i=ne[i]) //循环更新所有点的距离
{
if(dis[e[i]]>distance+w[i]) //源到直接该点的距离比经过当前最小点到该点的距离大
{
dis[e[i]]=distance+w[i]; //更新距离
heap.push({dis[e[i]],e[i]}); //更新距离,找到下一个当前最小距离点
}
}
}
else continue; //当前最小距离点已经是最小距离点,则直接进入下一层循环
}
if(dis[n]==0x3f3f3f3f) return -1;
else return dis[n];
}
int main()
{
memset(h,-1,sizeof h);
memset(dis,0x3f,sizeof dis);
cin>>n>>m;
while(m--)
{
int a,b,c;
cin>>a>>b>>c;
add(a,b,c);
}
cout<<dijkstraHeap()<<endl;
system("pause");
return 0;
}