u010585135的专栏

参考资料:《算法导论》第24章:单源最短路径,《数据结构(C++语言版)》(邓俊辉)第六章:图

单源最短路径算法,有两种比较经典的算法:

一种是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 DEBUGint main(){#ifdef DEBUGfreopen("input.txt","r",stdin);#endifGraphMatrix 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 6A C 7B D 5D B -2B C 8C D -3C E 9E A 2E D 7B E -4对于Bell-Ford算法的测试程序:

下午某时,天气晴,我在某地,想念你。

u010585135的专栏

相关文章:

你感兴趣的文章:

标签云: