目录
Dijkstra介绍
迪杰斯特拉算法(Dijkstra)是由荷兰计算机科学家狄克斯特拉于1959 年提出的,因此又叫狄克斯特拉算法。这是从一个顶点到其余各顶点的最短路径算法,解决的是有权图中最短路径问题。迪杰斯特拉算法主要特点是从起始点开始,采用贪心算法的策略,每次遍历到始点距离最近且未访问过的顶点的邻接节点,直到扩展到终点为止。
-
算法思想
dijkstra采用迭代的办法,贪心算法的原则,每一次都将改变更新源点到不同点的最小边权,遍历没有确定最优解的点。
由于需要逐个找到最小权值O(n),并且在在此基础上更新其他点的最小权值O(n/2),因而算法空间复杂度为O(n
)
-
具体步骤
- n为点的个数,m为边的个数,start为源点序号,map[][]存图(初始化map为INF无穷大)
- 初始化visit[]为0,表示所有点到源点的距离的最优解尚未确定;其中令visit[start]=1,表示源点的最优解已知无需确定
- 初始化dis[i]表示当前已知到源点最小权值(易知visit[i]==true时即得到i点最优解)
- 第一次迭代时,找到min(dis[i]),visited[i]==true;
- 第x次迭代时,首先找到min(dis[i])(其中visit[i]==0),然后标记该点i为pos。接下来对于每一个与pos相连(map[pos][j]<INF)且visit[j]==0的点,比较dis[j]与dis[pos]+map[pos][j],让dis[pos]=其中较小的一个,完成本次更新
-
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 1000010
using namespace std;
const int INF=1e9+7;
int n;//point
int m;//edge
bool start;//source point
int map[10000][10000];//memory map
int dis[MAXN];//best way
int visit[MAXN];//whether visited
void init(){
memset(visit,false,sizeof(visit));
for(int i=1;i<=n;i++){
map[i][i]=0;
}
visit[start]=true;
for(int i=1;i<=n;i++){
dis[i]=map[start][i];
}
}
void dijkstra(){
for(int i=1;i<=n;i++)
{
int pos;
int min=INF;
for(int i=1;i<=n;i++){
if(!visit[i]&&min>dis[i]){
min=dis[i];
pos=i;
}
}
visit[pos]=true;
for(int i=1;i<=n;i++){
if(!visit[i]&&map[pos][i]<INF&&dis[i]>(dis[pos]+map[pos][i])){
dis[i]=dis[pos]+map[pos][i];
}
}
}
}
int main()
{
memset(map,INF,sizeof(map));
scanf("%d%d",&n,&m);
scanf("%d",&start);
for(int i=1;i<=m;i++){
int u,v,w;
cin>>u>>v>>w;
map[u][v]=w;
}
init();
dijkstra();
for(int i=1;i<=n;i++){
cout<<dis[i]<<' ';
}
return 0;
}
Dijkstra链式向前星
-
优化思路
- 在基础dijkstra的代码中,我们采用了以点为核心的转移和存储
- 显然这种方法
很low容易出现时间复杂度和空间复杂度的不便裂开qwq- 因此我们找到一种
更为方便的方式——以边为核心进行转移和存储
-
实现步骤
- 大体的实现途径与基本dijkstra算法一致
- 在记录时构造结构体Edge[]——next表示与改变同一起点的上一条边,to表示该边对应终点,w表示该边的权值————同时在结构体外定义tot表示边的编号
- 定义一个head[u]表示与u点相连的前一条边编号,初始化head[]=-1(后续起到关键作用)
在读入时采用add(u,v,w)建边 - 在add中,tot为该边的编号;实现next时,edge[tot]=head[u]表示以该边的起点u为起点的上一条边。完成复制后head[u]++使得head[u]即为该边的编号
值得注意的是,当edge[i]==-1时,说明以该边起点为起点的边已经遍历完(在执行过程中是链式向前星关键的一个判断条件)
-
代码实现
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<memory.h>
#define MAXN 1000010
using namespace std;
const int INF=1e9+77;
int n;//point
int m;//edge
int start;//source point
int dis[MAXN];//best way
bool visit[MAXN];//whether visited
typedef struct Edge{
int next;//next edge with the same head[]
int to;//next point
int w;//len
}Edge;
Edge edge[MAXN];
int head[MAXN];
int tot;
void init()
{
memset(dis,INF,sizeof(dis));
memset(visit,false,sizeof(visit));
memset(head,-1,sizeof(head));
tot=0;
}
void add(int u,int v,int wi)
{
edge[tot].to =v;
edge[tot].w =wi;
edge[tot].next =head[u];
head[u]=tot++;
}
void dijkstra(int s){
int min,pos;
while(1){
min=INF;
pos=-1;
for(int i=1;i<=n;i++)
{
if(!visit[i]&&min>dis[i])
{
min=dis[i];
pos=i;
}
}
if(pos==-1)
{
break;
}
visit[pos]=true;
for(int i=head[pos];i!=-1;i=edge[i].next )
{
int t=edge[i].to ;
if(!visit[t]&&dis[t]>(dis[pos]+edge[i].w ))
{
dis[t]=dis[pos]+edge[i].w ;
}
}
}
return;
}
int main()
{
init();
scanf("%d%d%d",&n,&m,&start);
dis[start]=0;
for(int i=1;i<=m;i++)
{
int u,v,wi;
scanf("%d%d%d",&u,&v,&wi);
add(u,v,wi);
add(v,u,wi);
}
dijkstra(start);
for(int i=1;i<=n;i++)
{
cout<<dis[i]<<' ';
}
return 0;
}
Dijkstra链式向前星堆优化
优化思路
- 在朴素版dijkstra向链式向前星进行优化的过程中,我们巧妙的使用以点计数变为以边计数的优化方式缓解了空间的压力
- 而在链式向前星向堆优化的过程中,我们则借助“堆”这一巧妙地结构来完成
- 这里用到小根堆这个概念
- 因为采用贪心策略进行迭代,而贪心的原则就是选择最近的点,故可以使用小根堆
参考文章
本文链接:c++优先队列(priority_queue)用法详解_吕白_的博客-CSDN博客_c++ priority_queue
本文链接:数据结构之堆_Hidden.Blueee的博客-CSDN博客_堆
优化步骤
- 建立一个结构体小根堆
- 在每次迭代时取出堆顶元素,记录并删除
- 以堆顶元素为中心对未访问的结点进行贪心更新
代码实现
(洛谷P4779)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define MAXN 1000010
using namespace std;
inline int read(){
int f = 1, x = 0;char ch = getchar();
while (ch > '9' || ch < '0'){if (ch == '-')f = -f;ch = getchar();}
while (ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
return x * f;
}
typedef struct Edge{
int w;
int next;
int to;
}E;
E edge[MAXN];
const int INF=1e9+77;
int n;//点
int m;//edge
int s;//source point
int dis[MAXN];
bool visit[MAXN];
int head[MAXN];
int cnt=0;
struct node{
int dis;
int pos;
bool operator < (const node &x)const{
return x.dis <dis;
}
};
void add(int u,int v,int w){
cnt++;
edge[cnt].w =w;
edge[cnt].to=v;
edge[cnt].next =head[u];
head[u]=cnt;
}
void init(){
memset(dis,INF,sizeof(dis));
memset(visit,false,sizeof(visit));
memset(head,-1,sizeof(head));
}
std::priority_queue<node> q;
void dijkstra(){
dis[s]=0;
q.push((node){0,s});
while(!q.empty()) {
node tmp=q.top() ;
q.pop() ;
int value=tmp.dis ,posn=tmp.pos ;
if(visit[posn]){
continue;
}
visit[posn]=true;
for(int i=head[posn];i!=-1;i=edge[i].next){
int t=edge[i].to ;
if(dis[t]>dis[posn]+edge[i].w ){
dis[t]=dis[posn]+edge[i].w ;
if(!visit[t]){
q.push((node){dis[t],t});
}
}
}
}
}
int main()
{
init();
n=read(),m=read(),s=read();
for(int i=1;i<=m;i++){
int u,v,w;
u=read();
v=read();
w=read();
add(u,v,w);
}
dijkstra();
for(int i=1;i<=n;i++){
printf("%d ",dis[i]);
}
return 0;
}
参考资料(帮助理解代码)
本文链接:C语言中的typedef struct用法_华华华-CSDN博客
原文链接:[C++基础]队列<queue>中的常用函数 - Horstxu - 博客园
本文链接:强制转换_陌陌Jun的博客-CSDN博客