dijkstra算法:寻找到全图各点的最短路径

dijkstra算法介绍:即迪杰斯特拉算法,是从一个顶点到其余各顶点的最短路径算法,解决的是有向图中最短路径问题。迪杰斯特拉算法主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止,是一种广度优先的搜索方法。
dijkstra算法原理:最优子路径存在。假设从S→E存在一条最短路径SE,且该路径经过点A,那么可以确定SA子路径一定是S→A的最短路径。证明:反证法。如果子路径SA不是最短的,那么就必然存在一条更短的'SA,从而SE路径也就不是最短,与原假设矛盾。
dijkstra算法缺点:与此前说过的viterbi不同,此算法能够求出从起点到其余每个结点的最短路径,所以需要遍历所有的路径和结点,计算复杂度比较大。
dijkstra算法例子:求从结点0到各个结点的最短路径。

dijkstra算法:寻找到全图各点的最短路径_第1张图片

 

step1:首先建立两个集合S={}:表示已经找到最短路径的结点;U={}:表示尚未找到最短路径的结点。显然,S与U互为补集,S U U=所有结点组成的集合,当U为空的时候算法结束,所有结点最短路径均已找到。
step2:建立一个数组dist[i],用于存放起点0到该结点i的最短路径(可能需要更新,接下来会解释)。然后再建立一个布尔数组s[i](初值均为0),用于表示该结点是否已经找到最短路径,如已经找到便不再遍历。
step3:具体执行部分:
A:
初始点设定。
对于结点0,首先将其纳入到S集合中,然后寻找并计算与结点0直接相连路径的长度,即dist[1]=100,dist[30]=2,dist[4]=10(这时dist[0]=0)。而不能直接到达的结点距离为无限大∞。这里使用dist[3]=99999,方便程序比较大小。然后使s[0]=1,表示已经遍历过该结点。

B:选取最小dist[i]。比较dist[1],dist[3],dist[4]的长度,选择长度最短的dist[4],并将结点4纳入到S集合中,令s[4]=1,表明0到4的最短路径已经找到,且值为10。原因:最优子路径存在原理。由于dist[1],dist[3]均大于dist[4],所以若选择走经过结点1、3到达结点4路径,无论如何也不可能找到一条小于直接从结点0到结点4的路径!这个结论非常非常非常重要,是理解这个算法的关键!后面会反复用到,每一轮循环都要比较并选取最小的dist[i]。
C:更新dist[i]。现在,我们开始以结点4为中心向外扩展(广度优先)。现在,结点4可以到达结点3了,也表明从结点0可以通过结点4到达结点3了。至于要更新dist[i]的原因如下图:
在第一次选择中,我们纳入了起点A,然后由于dist[C]=6

dijkstra算法:寻找到全图各点的最短路径_第2张图片
 dijkstra算法:寻找到全图各点的最短路径_第3张图片

step4:重复上述步骤B、C,直到U集合清空,s[i]中所有值均为1。这就表明图中所有点都找到了最短路径。

下面放出以上例子的步骤表,如果能理解就表明基本了解dijkstra算法的思想了。

 dijkstra算法:寻找到全图各点的最短路径_第4张图片

dijkstra算法:寻找到全图各点的最短路径_第5张图片

最后,从结点0到各个点的最短路径就都算出来了。

dijkstra算法重点:理解为何需要选取最小的dist[i];理解为何需要更新dist[i]。

  

  1 #include 
  2 using namespace std;
  3  
  4 const int maxnum = 100;
  5 const int maxint = 999999;
  6  
  7  
  8 void Dijkstra(int n, int v, int *dist, int *prev, int c[maxnum][maxnum])
  9 {
 10     bool s[maxnum];    // 判断是否已存入该点到S集合中
 11     
 12     // 确认起始节点并设置相关参数
 13     for(int i=1; i<=n; ++i)
 14     {
 15         dist[i] = c[v][i];     // 将邻接矩阵中数据传入dist[]中
 16         s[i] = 0;     // 初始所有点都未纳入S中
 17         if(dist[i] == maxint)
 18             prev[i] = 0;     // 该点暂时没找到前驱结点
 19         else
 20             prev[i] = v;     // 该点的前驱结点为起始点v
 21     }
 22     dist[v] = 0;
 23     s[v] = 1;
 24  
 25     // 依次将未放入S集合的结点中,取dist[]最小值的结点,放入结合S中
 26     // 一旦S包含了所有V中顶点,dist就记录了从源点到所有其他顶点之间的最短路径长度
 27     for(int i=2; i<=n; ++i)
 28     {
 29         int tmp = maxint;
 30         int u = v;
 31         
 32         // 找出当前未使用的点j的dist[j]最小值
 33         for(int j=1; j<=n; ++j)
 34             if((!s[j]) && dist[j]<tmp)
 35             {
 36                 u = j;              // u保存当前邻接点中距离最小的点的号码
 37                 tmp = dist[j];
 38             }
 39         s[u] = 1;    // 表示u点已存入S集合中
 40  
 41         // 更新dist,用更小的值代替原先的值
 42         for(int j=1; j<=n; ++j)
 43             if((!s[j]) && c[u][j]<maxint)
 44             {
 45                 int newdist = dist[u] + c[u][j];
 46                 if(newdist < dist[j])
 47                 {
 48                     dist[j] = newdist;
 49                     prev[j] = u;
 50                 }
 51             }
 52     }
 53 }
 54 
 55 // 记录下这条最短路径的路线(经过的结点) 
 56 void searchPath(int *prev,int v, int u)
 57 {
 58     int que[maxnum];
 59     int tot = 1;
 60     que[tot] = u;
 61     tot++;
 62     int tmp = prev[u];
 63     while(tmp != v)
 64     {
 65         que[tot] = tmp;
 66         tot++;
 67         tmp = prev[tmp];
 68     }
 69     que[tot] = v;
 70     for(int i=tot; i>=1; --i)
 71         if(i != 1)
 72             cout << que[i] << " -> ";
 73         else
 74             cout << que[i] << endl;
 75 }
 76  
 77 int main()
 78 {
 79     // 各数组都从下标1开始
 80     int dist[maxnum];     // 表示当前点到源点的最短路径长度
 81     int prev[maxnum];     // 记录当前点的前一个结点
 82     int c[maxnum][maxnum];   // 记录图的两点间路径长度
 83     int n, line;             // 图的结点数和路径数
 84  
 85     // 输入结点数
 86     cin >> n;
 87     // 输入路径数
 88     cin >> line;
 89     int p, q, len;          // 输入p, q两点及其路径长度
 90  
 91     // 初始化c[][]为maxint
 92     for(int i=1; i<=n; ++i)
 93         for(int j=1; j<=n; ++j)
 94             c[i][j] = maxint;
 95  
 96     for(int i=1; i<=line; ++i)  
 97     {
 98         cin >> p >> q >> len;
 99         if(len < c[p][q])       // 有重边
100         {
101             c[p][q] = len;      // p指向q
102             c[q][p] = len;      // q指向p,这样表示无向图
103         }
104     }
105  
106     for(int i=1; i<=n; ++i)
107         dist[i] = maxint;
108     for(int i=1; i<=n; ++i)
109     {
110         for(int j=1; j<=n; ++j)
111             printf("%8d", c[i][j]);
112         printf("\n");
113     }
114     
115     // 假定起点为结点1
116     Dijkstra(n, 1, dist, prev, c);
117  
118     // 最短路径长度
119     cout << "源点到最后一个顶点的最短路径长度: " << dist[n] << endl;
120  
121     // 路径
122     cout << "源点到最后一个顶点的路径为: ";
123     
124     // 假定起点为结点1
125     searchPath(prev, 1, n);
126 }

 

 

 


作者:俊爷拒做学渣
链接:https://www.jianshu.com/p/c9b27617502e

转载于:https://www.cnblogs.com/Tangent-1231/p/8477852.html

你可能感兴趣的:(dijkstra算法:寻找到全图各点的最短路径)