单源最短路径问题是指,给定一个图G=(V,E),但愿找到从给定源结点s到每一个节点v的最短路径。单源最短路径问题能够用来解决不少最短路径的变体。算法
单目的地最短路径问题:找到从每一个结点v到给定目的地结点t的最短路径。将图的每条边翻转,这个问题能够转换为单源最短路径问题。函数
单结点对最短路径问题:找到从给定结点u到给定结点v的最短路径。若是已经解决了u的单元最短路径问题,则该问题已经解决。优化
在单源最短路径问题中,根据图的性质不一样有不一样的解决方案。主要的性质有:是有向图仍是无向图,是权重图仍是单位图,有无负权重的边,是否有环。spa
Dijkstra算法解决的是非负权重有向图上的单源最短路径问题。算法逻辑为维持一个结点集合S,集合中每一个结点之间的最短路径已经被找到,重复从集合V-S中选择与源结点r之间最短路径估计最小的结点u加入到S,而后对全部从u出发的边检查r到u的距离加上该边的距离是否小于该边另外一个结点本来的最短路径估计。code
如图(a)初始状态,s是源结点,加入到集合S中blog
(b)检查s出发的路径(s,t)和(s,y)更新y和t的最短路径估计排序
(c)在剩下结点中选择最短路径估计最小的结点y,而后检查从y出发的路径(y,t)+(s,y)=8<10,更新t的距离为8,一样对x和z也进行一样的操做,结束后y加入到S中队列
(d)接下来选择z结点,(z,s)+7=14>s,因此s保持不变,而(z,x)+6=13<14,x的值更新为13,而后将z加入到S中。it
(e)-(f)的操做同上,再也不作详细描述。最终全部点都加入到了S中。io
为了完成算法,咱们须要存储每一个结点的最短路径估计和他们的前驱结点来输出最终路径,同时须要一个最小优先队列来保存V-S中结点的最短路径估计排序,不然须要遍历V-S中的结点来选择出最短的那个。直接遍历全部边的算法复杂度为O(V^2),若使用最小二叉堆来存储优先队列则复杂度能够下降为O((V+E)lgV)。
1 #include<stdio.h> 2 using namespace std; 3 #define SIZE 10 4 #define INFI 10000 5 6 int G[SIZE][SIZE];//邻接矩阵,参数初始化略 7 int dist[SIZE];//与根间的距离估计 8 bool visit[SIZE];//是否被访问过 9 10 void dijkstra(int root){ 11 int i,j; 12 int min; 13 for(i = 0; i < SIZE; i++){ 14 dist[i] = INFI; 15 visit[i] = false; 16 } 17 dist[root] = 0; 18 for(i = 0; i <SIZE; i++){ 19 min = 0;//寻找剩余点中距离根最近的点 20 for(j = 1; j < SIZE;j++){ 21 if(!visit[j] && dist[j] < dist[min]){ 22 min = j; 23 } 24 } 25 for(j = 0; j < SIZE; j++){ 26 if(!visit[j] && G[min][j] + dist[min] < dist[j]) 27 dist[j] = G[min][j] + dist[min];//检查是否须要更新最短路径 28 } 29 visit[min] = true; 30 } 31 }
该算法能够解决负权重边的图,算法返回一个布尔值,代表是否存在一个从源节点能够到达的权重为负的环路,若存在则算法将告诉咱们不存在解决方案,由于这个环路的存在会致使出现距离为负无穷的点。
算法须要进行|V|-1次循环,每次循环按照一样的顺序对全部边进行松弛,结束后检查是否存在权重为负的环路。算法复杂度为O(VE),所以该算法在处理密集图时效率会下降,整体效率不如dijkstra算法。
如图所示每一次松弛边的顺序都是(t,x) (t,y) (t,z) (y,x) (y,z) (z,x) (z,s) (s,t) (s,y)。源结点为s,加了阴影的边表示前驱路径。
(a) 更新源结点的距离为0
(b) 按照顺序,仅(s,t) (s,y)能够更新结点的值,t=6 y=7
(c) (t,z) (y,x)能够更新z和x的值
(d) (x,t)能够更新t的值
(e) 本次循环没有能够更新的值,检查不存在权值为负的环返回TRUE
1 #include<stdio.h> 2 using namespace std; 3 #define SIZE 10 4 #define INFI 10000 5 6 int G[SIZE][SIZE];//邻接矩阵,参数初始化略 7 int dist[SIZE];//与根间的距离估计 8 int p[SIZE];//前驱结点 9 10 void bellmanFord(int root){ 11 int i, j, k; 12 for(i = 0; i < SIZE; i++){ 13 dist[i] = INFI; 14 } 15 dist[root] = 0; 16 for(i = 0; i < SIZE - 1; i++){ 17 for(j = 0; j < SIZE; j++){ 18 if(dist[j] == INFI) 19 continue;//结点没法从源结点到达则先跳过 20 for(k = 0; k < SIZE; k++){ 21 if(G[j][k] != 0 && G[j][k] + dist[j] < dist[k]) 22 dist[k] = G[j][k] + dist[j];//松弛路径 23 } 24 } 25 } 26 for(i = 0; i < SIZE; i++){ 27 for(j = 0; j < SIZE; j++){ 28 if(G[i][j] != 0 && dist[i] > dist[j] + G[i][j]) 29 return false;//检查有无权重为负的环 30 } 31 } 32 }
在线性规划问题中,一般会给定一个m*n的矩阵A、一个m维的向量b和一个n维向量c,但愿找到一个n维向量x,使得在有Ax≤b给定的m个约束条件下优化目标函数,
使目标函数值最大。在一个差分约束系统中,线性规划矩阵A的每个行包括一个1和一个-1,其余全部项都是0,每一个限制条件变为不等式xj-xi≤bk。
如图所示的矩阵和向量能够表示为8个简单不等式,要求出一组可行解。以x做为结点,后面的约束值做为路径权重,能够画出一张约束图
能够经过对约束图设定一个源结点,如v0求出最短路径解来得到一组可行解。由于含有负值权重,因此须要经过Bellman-Ford算法来进行求解。