最短路径之Dijkstra算法

简介: 最短路径之Dijkstra算法

引入

20210517152501363.png

迪科斯彻提出了著名的单源最短路径求解算法——Dijkstra算法。

Dijkstra算法是解决单源最短路径问题的贪心算法,它先求出长度最短的一条路径,再参照该最短路径求出长度次短的一条路径,直到求出从源点到其他各个顶点的最短路径。

Dijkstra算法的基本思想是首先假定源点为u,顶点集合V被划分为两部分:集合S和V-S。初始时S中仅含有源点u,其中S中的顶点到源点的最短路径已经确定。集合V-S中所包含的顶点到源点的最短路径的长度待定,称从源点出发只经过S中的点到达V-S中的点的路径为特殊路径,并用数组dist[]记录当前每个顶点所对应的最短特殊路径长度.


Dijkstra算法采用的贪心策略是选择特殊路径长度最短的路径,将其连接的V-S中的顶点加入到集合S中,同时更新数组dist[]。一旦S包含了所有顶点,dist[]就是从源到所有其他顶点之间的最短路径长度。


算法步骤:


数据结构。设置地图的带权郐接矩阵为G.Edge[][],即如果从源点u到顶点i有边,

就令G.Edge[u][i]等于<u,i>的权值,否则G.Edge[u][i]=∞(无穷大);采用一维数组dist[i]来记录从源点到i顶点的最短路径长度;采用一维数组p[i]来记录最短路径上i顶点的前驱。

初始化。令集合S={u},对于集合V-S中的所有顶点x,初始化dist[1]=G.Edge[u][i],

如果源点u到顶点i有边相连,初始化p[i]=u,否则p[i]=-1。

找最小。 在集合V-S中依照贪心策略来寻找使得dist[j]具有最小值的顶点t,即

dist[t]=min (dist [ j ] | j属于V-S集合),则顶点t就是集合V-S中距离源点u最近的

顶点。

加入S战队。将顶点t加入集合S中,同时更新V-S。

判结束。如果集合V-S为空,算法结束,否则转6。

借东风。在 3 中已经找到了源点到t的最短路径,那么对集合V-S中所有与顶点

t相邻的顶点j,都可以借助t走捷径。如果dis[j>dist[t]+G.Edge[]Ii], 则

dist[i]=dist[t]+G.Edge[ t ][ i ],记录顶点j的前驱为t,即p[i]=t,转3。


参考代码

#include<iostream>
#include<stack>
#include<string.h>
#include<string>
using namespace std;
const int MaxVnum=100; // 城市的个数可修改
const int INF=1e7; // 无穷大10000000
int dist[MaxVnum],p[MaxVnum];//最短距离和前驱数组
bool flag[MaxVnum]; //如果s[i]等于true,说明顶点i已经加入到集合S;否则顶点i属于集合V-S
typedef string VexType;  //顶点的数据类型,根据需要定义
typedef int EdgeType;  //边上权值的数据类型,若不带权值的图,则为0或1
typedef struct {
  VexType Vex[MaxVnum];
  EdgeType Edge[MaxVnum][MaxVnum];
  int vexnum,edgenum; //顶点数,边数
} AMGragh;
int locatevex(AMGragh G,VexType x) {
  for(int i=0; i<G.vexnum; i++) //查找顶点信息的下标
    if(x==G.Vex[i])
      return i;
  return -1;//没找到
}
void CreateAMGraph(AMGragh &G) {
  int i,j,w;
  VexType u,v;
  cout << "请输入顶点数:"<<endl;
  cin>>G.vexnum;
  cout << "请输入边数:"<<endl;
  cin>>G.edgenum;
  cout << "请输入顶点信息:"<<endl;
  for(int i=0; i<G.vexnum; i++) //输入顶点信息,存入顶点信息数组
    cin>>G.Vex[i];
  for(int i=0; i<G.vexnum; i++) //初始化邻接矩阵为无穷大
    for(int j=0; j<G.vexnum; j++)
      G.Edge[i][j]=INF;
  cout << "请输入每条边依附的两个顶点及权值:"<<endl;
  while(G.edgenum--) {
    cin>>u>>v>>w;
    i=locatevex(G,u);//查找顶点u的存储下标
    j=locatevex(G,v);//查找顶点v的存储下标
    if(i!=-1&&j!=-1)
      G.Edge[i][j]=w; //有向图邻接矩阵
    else {
      cout << "输入顶点信息错!请重新输入!"<<endl;
      G.edgenum++;//本次输入不算
    }
  }
}
void Dijkstra(AMGragh G,int u) {
  for(int i = 0; i < G.vexnum; i++) {//初始化源点u到其他各个顶点的最短路径长度
    dist[i] = G.Edge[u][i];//初始化dist
    flag[i] = false;
    if(dist[i]==INF) {
      p[i]=-1;//源点u到改顶点的路径长度为无穷大,说明顶点i与源点不相邻
    } else {
      p[i] = u;//说明顶点i与源点u相邻,设置顶点i的前驱p[i] = u;
    }
  }
  dist[u] = 0;
  flag[u] = true;//初始时,集合S中只有一个元素:源点u
  for(int i = 0; i < G.vexnum; i++) {
    int temp = INF,t = u;
    for(int j = 0; j  < G.vexnum; j++) { //集合V-S中寻找距离源点u最近的顶点t
      if(!flag[j]&&dist[j]<temp) {
        t = j;
        temp = dist[j];
      }
    }
    if(t==u) {
      return;//找不到t,说明V-S中已经没有节点了,跳出循环
    }
    flag[t] = true;//找到则将t加入集合
    for(int j = 0; j < G.vexnum; j++) { //跟新与t相邻接的顶点到源点u的距离
      if(!flag[j]&&G.Edge[t][j]<INF) {//依旧是更新V-S中的点   但必须是t->j的这种邻接点. 否则t的加入对j最短路径毫无影响
        if(dist[j]>(dist[t]+G.Edge[t][j])) {//判断在t的加入下,路径是否变短了
          dist[j] = dist[t]+G.Edge[t][j];
          p[j] = t;//跟新节点前驱
        }
      }
    }
  }
}
void findpath(AMGragh G,VexType u) {
  int x;
  stack<int> S;
  cout<<"源点为: "<<u<<endl;
  for(int i = 0; i < G.vexnum; i++) {
    x = p[i];
    if(x==-1&&u!=G.Vex[i]) {//dis的前驱为-1但不是源点则说明无路可达.
      cout<<"源点到"<<G.Vex[i]<< "无路可达!!!"<<endl;
      continue;
    }
    while(x!=-1) {
      S.push(x);//存放的是下标
      x = p[x];
    }
    cout<<"源点到"<<G.Vex[i]<<"顶点的最短路径为: ";
    while(!S.empty()) {
      cout<<G.Vex[S.top()]<<"---";
      S.pop();
    }
    cout<<G.Vex[i]<<"\t最短距离为:"<<dist[i]<<endl;
  }
}
int main() {
  AMGragh G;
  int st;
  VexType u;
  CreateAMGraph(G);
  cout<<"请输入源点的信息:"<<endl;
  cin>>u;
  st= locatevex(G,u);//查找源点u的存储下标
  Dijkstra(G,st);
  findpath(G,u);
  return 0;
}
/*
0 1 1
0 3 4
1 2 9
1 3 2
2 1 5
2 0 3
2 3 8
3 2 6
*/


运行结果:


20210517154418487.png

20210517154312485.png

算法分析:

20210517154515191.png

算法优化:

20210517154800749.png

下面是优先队列和链式前向星的优化:

#include<bits/stdc++.h>
using namespace std;
const int maxn = 100;//节点数 
const int INF = 0x3f3f3f3f;
int  n,m,start,cnt,num,head[maxn],dist[maxn],p[maxn];//head:节点数组   dist:存放最短距离,  p:存放前驱 
struct node{
  int to,next,w;
}edge[maxn*maxn];//边集数组 
void add(int u,int v,int w){
  edge[++num].next = head[u];
  edge[num].to = v;
  edge[num].w = w;
  head[u] = num;
} 
struct cmp{//仿函数,用于优先队列数据的比较 
  bool operator()(int &a,int &b){
    return dist[a]>dist[b];
  }
};
void Dijkstra(int x){
  //用优先队列寻找V-M中的最小值,来代替傻瓜式 搜索,节约时间
   priority_queue<int,vector<int>,cmp > Q;
   memset(dist,INF,sizeof(dist));
   memset(p,-1,sizeof(p));
   dist[x] = 0;
   Q.push(x); 
   while(!Q.empty()){
    int u = Q.top();
    Q.pop();
    for(int i = head[u]; i; i = edge[i].next){
      int v = edge[i].to;
      if(dist[v]==INF){//如果是无穷大,说明未在V-M中,则加入队列 
        Q.push(v);
       }
       if(dist[v]>dist[u]+edge[i].w) {
        dist[v]  = dist[u]+edge[i].w;
        p[v] = u;
        //cout<<u<<endl;
       }
     }
   }
} 
void findPath(int u){
  int x;
  stack<int> s;
  for(int i = 1; i <= n; i++){
    x = p[i];
    if(x==-1&&i!=u){
      cout<<"源点到"<<i<<"无路可达!!!"<<endl;
      continue; 
    }
    while(x!=-1){
      s.push(x);
      x = p[x];
    }
    cout<<"源点到"<<i<<"顶点的最短路径为: ";
    while(!s.empty()) {
      cout<<"--->"<<s.top();
      s.pop();
    }
    cout<<"\t最短距离为"<<dist[i]<<endl; 
  } 
}
int main(){
  int u,v,w;
  cin>>n>>m>>start;
  for(int i = 0; i < m; i++){
    cin>>u>>v>>w;
    add(u,v,w);
  } 
  Dijkstra(start);
  //cout<<"hahha"<<endl;
  findPath(start);
  return 0;
}

代码分析:易知每次寻找最小值都是在V-S中寻找的,第一次直接是源点不用寻找,然后更新dist,并将其加入到队列中,第二次是从队列中弹出一个最小的(不在队列中的V-S点不用考虑,因为他们都是无穷大,而我们要找的是最小值,),然后继续更新dist,每次在更新时,如果某个结点的dist为无穷大,则说明之前没有加入过,将其加入即可.然后如果在最小值的加入下该点的dist更小了,则更新dist和p.


相关文章
|
7月前
|
存储 运维 监控
基于 C# 语言的 Dijkstra 算法在局域网内监控软件件中的优化与实现研究
本文针对局域网监控系统中传统Dijkstra算法的性能瓶颈,提出了一种基于优先队列和邻接表优化的改进方案。通过重构数据结构与计算流程,将时间复杂度从O(V²)降至O((V+E)logV),显著提升大规模网络环境下的计算效率与资源利用率。实验表明,优化后算法在包含1000节点、5000链路的网络中,计算时间缩短37.2%,内存占用减少21.5%。该算法适用于网络拓扑发现、异常流量检测、故障定位及负载均衡优化等场景,为智能化局域网监控提供了有效支持。
195 5
|
4月前
|
机器学习/深度学习 编解码 算法
【机器人路径规划】基于迪杰斯特拉算法(Dijkstra)的机器人路径规划(Python代码实现)
【机器人路径规划】基于迪杰斯特拉算法(Dijkstra)的机器人路径规划(Python代码实现)
476 4
|
5月前
|
算法 机器人 定位技术
基于机器视觉和Dijkstra算法的平面建筑群地图路线规划matlab仿真
本程序基于机器视觉与Dijkstra算法,实现平面建筑群地图的路径规划。通过MATLAB 2022A读取地图图像,识别障碍物并进行路径搜索,支持鼠标选择起点与终点,最终显示最优路径及长度,适用于智能导航与机器人路径规划场景。
|
存储 人工智能 算法
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
这篇文章详细介绍了Dijkstra和Floyd算法,这两种算法分别用于解决单源和多源最短路径问题,并且提供了Java语言的实现代码。
977 3
数据结构与算法细节篇之最短路径问题:Dijkstra和Floyd算法详细描述,java语言实现。
|
11月前
|
存储 算法 测试技术
【狂热算法篇】探秘图论之 Floyd 算法:解锁最短路径的神秘密码(通俗易懂版)
【狂热算法篇】探秘图论之 Floyd 算法:解锁最短路径的神秘密码(通俗易懂版)
|
11月前
|
算法 编译器 C++
【狂热算法篇】探秘图论之Dijkstra 算法:穿越图的迷宫的最短路径力量(通俗易懂版)
【狂热算法篇】探秘图论之Dijkstra 算法:穿越图的迷宫的最短路径力量(通俗易懂版)
|
3月前
|
机器学习/深度学习 算法 机器人
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
【水下图像增强融合算法】基于融合的水下图像与视频增强研究(Matlab代码实现)
395 0
|
3月前
|
数据采集 分布式计算 并行计算
mRMR算法实现特征选择-MATLAB
mRMR算法实现特征选择-MATLAB
269 2
|
4月前
|
传感器 机器学习/深度学习 编解码
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
MATLAB|主动噪声和振动控制算法——对较大的次级路径变化具有鲁棒性
269 3
|
3月前
|
机器学习/深度学习 算法 机器人
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
使用哈里斯角Harris和SIFT算法来实现局部特征匹配(Matlab代码实现)
210 8

热门文章

最新文章