单源最短路径算法
2014-12-29 20:25
309 查看
参考资料:《算法导论》第24章:单源最短路径,《数据结构(C++语言版)》(邓俊辉)第六章:图
单源最短路径算法,有两种比较经典的算法:
一种是Dijkstra算法,此算法应用有限制,即只能用在图边的权重为正值的情况下,因为如果边权重为负值,则更新一个点的距离有可能需要进行两次访问,而Dijkstra算法对于图中的每个点都只进行了一次访问。时间复杂度为O((V+E)logV),这是在用优先级队列的情况下的最好的复杂度。
另一种是Bellman-Ford算法,此算法返回一个bool值,以表明是否存在一个从源节点可以到达的权重为负值的环路。如果存在这样一个环路,算法将告诉我们不存在解决方案。如果没有这种环路存在,算法将给出最短路径和它们的权重。Bellman-Ford算法共进行O(V)次迭代,每次迭代复杂度为O(E),因此总共的复杂度为O(VE),如果经过这么多次迭代以后还有某个顶点的权重比另一个顶点的权重和对应的一条边的权重之和大,则说明有环,返回false。
代码:
A B 6
A C 7
B D 5
D B -2
B C 8
C D -3
C E 9
E A 2
E D 7
B E -4
对于Bell-Ford算法的测试程序:
A B 6
A C 7
B D 5
D B -2
B C 8
C D -3
C E 9
E A 2
E D 7
B E -4
单源最短路径算法,有两种比较经典的算法:
一种是Dijkstra算法,此算法应用有限制,即只能用在图边的权重为正值的情况下,因为如果边权重为负值,则更新一个点的距离有可能需要进行两次访问,而Dijkstra算法对于图中的每个点都只进行了一次访问。时间复杂度为O((V+E)logV),这是在用优先级队列的情况下的最好的复杂度。
另一种是Bellman-Ford算法,此算法返回一个bool值,以表明是否存在一个从源节点可以到达的权重为负值的环路。如果存在这样一个环路,算法将告诉我们不存在解决方案。如果没有这种环路存在,算法将给出最短路径和它们的权重。Bellman-Ford算法共进行O(V)次迭代,每次迭代复杂度为O(E),因此总共的复杂度为O(VE),如果经过这么多次迭代以后还有某个顶点的权重比另一个顶点的权重和对应的一条边的权重之和大,则说明有环,返回false。
代码:
//GraphMatrix.h #ifndef __GraphMatrix__ #define __GraphMatrix__ #include<iostream> #include<vector> #include<deque> using namespace std; struct Vertex { char data; bool visited; int weight;//Dijkstra算法保存当前节点到源节点的边的总权重 Vertex* parent; Vertex(char d=0) { weight=INT_MAX; data=d; parent=0; visited=false; } bool operator<(const Vertex &a)const { return a.weight<weight; } }; struct Edge { int weight; Edge(int w=INT_MAX) { weight=w; } }; class GraphMatrix { int n; int e; vector<Vertex> V;//顶点集合 vector<vector<Edge>>E;//边的集合 int relax(int k); void printMessage(); public: GraphMatrix() { n=e=0; } int insertVertex(Vertex & v); void insertEdge(Vertex from,Vertex to,Edge e,bool doubleSide=true); void dijkstra(Vertex v); bool bellmanFord(Vertex v); }; #endif
//GraphMatrix.cpp #include"GraphMatrix.h" #include<queue> using namespace std; int GraphMatrix::insertVertex(Vertex & v) { V.push_back(v); E.push_back(vector<Edge>(V.size())); if(E.size()>1) for(int i=0;i<V.size()-1;i++) E[i].push_back(Edge()); n++; return V.size()-1; } void GraphMatrix::insertEdge(Vertex from,Vertex to,Edge e,bool doubleSide) { int k=0; int p=-1,q=-1; for(int i=0;i<V.size();i++) { if(V[i].data==from.data) { k+=1; p=i; } if(V[i].data==to.data) { k+=2; q=i; } } if((k&1)==0) p=insertVertex(from); if((k&2)==0) q=insertVertex(to); E[p][q]=e; if(doubleSide) E[q][p]=e;//如果不屏蔽则是无向图 this->e++; } //如果是没有权重的图,或者各个边权重相等,则BST也是求 //单源最短路径的算法 //注意:dijkstra算法智能用于边的权重为正的情况,因为每个节点 //只能被访问一次,如果有负边,则需要访问多次来更新那个顶点 void GraphMatrix::dijkstra(Vertex v) { int k=-1; for(int i=0;i<V.size();i++) if(V[i].data==v.data) { k=i; break; } V[k].parent=&V[k]; V[k].weight=0; V[k].visited=true; int index=relax(k); while(index>=0) { V[index].visited=true; index=relax(index); } printMessage(); } int GraphMatrix::relax(int k)//用这个函数来更新节点到原点的距离,并且返回最小值 { int m=INT_MAX,index=-1; for(int i=0;i<E.size();i++) { if(E[k][i].weight!=INT_MAX && V[i].visited==false && V[i].weight>V[k].weight+E[k][i].weight) { V[i].parent=&V[k]; V[i].weight=V[k].weight+E[k][i].weight; } if(V[i].visited==false && m>V[i].weight) { m=V[i].weight; index=i; } } return index; } bool GraphMatrix::bellmanFord(Vertex v) { for(int i=0;i<V.size();i++) { V[i].weight=INT_MAX; if(V[i].data==v.data) { V[i].weight=0; V[i].parent=nullptr; } } for(int i=0;i<V.size();i++) for(int j=0;j<E.size();j++) for(int k=0;k<E[j].size();k++) { //注意此处的判断,一个正无穷大的数和一个小的正数相加会变成负数!!! if(V[j].weight!=INT_MAX && E[j][k].weight!=INT_MAX && V[k].weight>V[j].weight+E[j][k].weight) { V[k].weight=V[j].weight+E[j][k].weight; V[k].parent=&V[j]; } } for(int j=0;j<E.size();j++) for(int k=0;k<E[j].size();k++) { //注意此处的判断,一个正无穷大的数和一个小的正数相加会变成负数!!! if(V[j].weight!=INT_MAX && E[j][k].weight!=INT_MAX && V[k].weight>V[j].weight+E[j][k].weight) return false; } printMessage(); return true; } void GraphMatrix::printMessage() { vector<Vertex> temp; for(int i=0;i<V.size();i++) { Vertex *v=&V[i]; while(v->parent!=nullptr) { temp.push_back(*v); v=v->parent; } temp.push_back(*v); while(!temp.empty()) { cout<<temp.back().data<<" "; temp.pop_back(); } cout<<V[i].weight<<endl; } }对于Dijkstra算法的测试:
#include<iostream> #include"GraphMatrix.h" #include<queue> #include<deque> using namespace std; #define DEBUG int main() { #ifdef DEBUG freopen("input.txt","r",stdin); #endif GraphMatrix gm; char from,to; int e; //测试边和顶点的插入 cout<<"输入边"<<endl; while(cin>>from>>to>>e) { cout<<"输入边"<<endl; gm.insertEdge(from,to,e);//调用默认构造函数 } gm.dijkstra('A'); system("pause"); return 0; }测试数据:为双向边
A B 6
A C 7
B D 5
D B -2
B C 8
C D -3
C E 9
E A 2
E D 7
B E -4
对于Bell-Ford算法的测试程序:
#include<iostream> #include"GraphMatrix.h" #include<queue> #include<deque> using namespace std; #define DEBUG int main() { #ifdef DEBUG freopen("input.txt","r",stdin); #endif GraphMatrix gm; char from,to; int e; //测试边和顶点的插入 cout<<"输入边"<<endl; while(cin>>from>>to>>e) { cout<<"输入边"<<endl; gm.insertEdge(from,to,e,false);//调用默认构造函数 } if(!gm.bellmanFord('A')) cout<<"存在权重为负值的环路!"<<endl; system("pause"); return 0; }测试用例:单向边
A B 6
A C 7
B D 5
D B -2
B C 8
C D -3
C E 9
E A 2
E D 7
B E -4
相关文章推荐
- 单源最短路径算法--Dijkstra算法和Bellman-Ford算法
- Bellman-Ford 算法实现单源最短路径
- 单源最短路径的Bellman-Ford 算法
- 堆优化 Dijstra单源最短路径算法 2(邻接表)
- Ford算法(单源最短路径)
- 算法导论第二十四章-单源最短路径-Cpp代码实现
- Dijkstra算法求单源最短路径Java实现
- Dijkstra算法,Bellman-Ford算法和BFS算法解决有向图的单源最短路径问题
- 单源最短路径算法模板(Dijkstra+BellmanFrod)
- Dijkstra 算法求单源最短路径
- 关于图的常用算法——Dijkstra单源最短路径、Floyd多源最短路径、Prim和Kruskal最小生成树算法
- 图论;单源最短路径;拓扑排序+松弛(有向无回路);Bellman-Ford(回路,负权回路);Dijkstra(无负权,可回路);可以用最小堆实现算法的优化;
- 单源最短路径算法
- 最短路径算法Dijistra算法—单源最短
- 图—单源最短路径算法(二)Dijksta算法
- 算法导论 第二十四章:单源最短路径
- 算法导论-第24章- 单源最短路径 - 24.1 Bellman-Ford 算法
- 单源最短路径算法模板(Dijkstra+BellmanFrod)
- POJ 1860 Currency Exchange Bellman-Ford算法求单源最短路径并判断是否有正权回路
- 单源最短路径算法 Bellman-Ford && SPFA 及 最短路算法统一归纳