您的位置:首页 > 其它

单源最短路径算法

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。

代码:

//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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: