#include<iostream>
#include<fstream>
#include<stack>
#include<queue>
using namespace std;
//定义边结构体类型
struct Edge
{
int start;//一条边的起始顶点编号
int end;//一条边的终止顶点编号
int value;//边的权值
Edge* next;//第start起点的链表的下一条边
Edge(int s,int e,int v):start(s),end(e),value(v),next(NULL){}
};
//定义顶点结构体类型
struct Vertex
{
int d;//点到源点之间的距离
int indegree;//顶点的入度
int Pie;//点的前驱点
Edge* head;
Vertex():head(NULL),d(0x7fffffff),Pie(0){}
};
//定义图类
class graph
{
private:
int n;//顶点个数
queue<int> Q;//拓扑排序队列
Vertex *V;//邻接链表,相当于动态数组Vertex V[n+1];V[1].head代表编号为1的点的链表,head为链表的第一个结点,每个结点代表一条边有start和end
public:
graph(int num):n(num){V=new Vertex[n+1];}//构造函数,初始化
~graph(){delete []V;}//析构函数
void creatDirectedGraph();//创建有向图
void creatUndirectedGraph();//创建无向图
void addSingleEdge(int start,int end,int value);//把当前边(start,end)插入到编号为start的点的链表中,有向图边的插入
void addDoubleEdge(int start,int end,int value);//无向图边的插入
bool haveEdge(int u,int v);//判断从u到v是否有一条边(编号)
void findInDegree();//计算所有顶点的入度
bool TopoSort();//有向无环图拓扑排序,判断AOV网是否存在有向回路
void DAGShortestPath(int s);
void printPathLength(int s);
void printPath(int s,int v);
};
//创建有向图
void graph::creatDirectedGraph()
{
cout<<"输入图的邻接表:"<<endl;
for(int i=1;i<=n;i++)
{
cout<<"输入第"<<i<<"个顶点的后继顶点信息:终止顶点编号e 权值v(结束条件是e<=0)"<<endl;
int end,value;
cin>>end>>value;
while(end>=0)
{
addSingleEdge(i,end,value);
cin>>end>>value;
}
}
return;
}
//创建无向图
void graph::creatUndirectedGraph()
{
cout<<"输入图的邻接表:"<<endl;
for(int i=1;i<=n;i++)
{
cout<<"输入第"<<i<<"个顶点的后继顶点信息:终止顶点编号e 权值v(结束条件是e<=0)"<<endl;
int end,value;
cin>>end>>value;
while(end>=0)
{
addDoubleEdge(i,end,value);
cin>>end>>value;
}
}
return;
}
//有向图边的插入
void graph::addSingleEdge(int start,int end,int value)
{
Edge *newEdge=new Edge(start,end,value);
//如果编号为start的点的链表为空或者它的第一个结点的end大于要插入的边的end,则把要插入的边放到第一个(保证每个点的链表的边的end按从小到大的顺序排列
if(V[start].head==NULL||V[start].head->end>end)
{
newEdge->next=V[start].head;
V[start].head=newEdge;
}
else
{
Edge *e=V[start].head,*pre=e;
//搜索要插入的位置,pre的end要小于要插入边的end,e的end大于等于要插入边的end
while(e!=NULL&&e->end<end)
{
pre=e;
e=e->next;
}
//遇到重复的边,不插入
if(e&&e->end==end)
{
delete newEdge;
return;
}
//否则插入到pre与e之间
newEdge->next=e;
pre->next=newEdge;
}
return;
}
//无向图边的插入
void graph::addDoubleEdge(int start,int end,int value)
{
addSingleEdge(start,end,value);
addSingleEdge(end,start,value);
return;
}
//判断从u到v是否有一条边(编号)
bool graph::haveEdge(int u,int v)
{
Edge* e=V[u].head;
while(e!=NULL)
{
if(v==e->end) return true;
else e=e->next;
}
return false;
}
//计算所有顶点的入度
void graph::findInDegree()
{
int u,v;
for(u=1;u<=n;u++) V[u].indegree=0;
for(v=1;v<=n;v++)
{
for(u=1;u<=n;u++)
{
if(haveEdge(u,v)) V[v].indegree++;
}
}
return;
}
//有向无环图拓扑排序
bool graph::TopoSort()
{
stack<int> s;
findInDegree();
for(int i=1;i<=n;i++)
{
if(V[i].indegree==0) s.push(i);//入度为0则入栈
}
int count=0;//记录输出的顶点数
while(!s.empty())
{
int u=s.top();
s.pop();//取出一个入度为0的顶点
Q.push(u);
++count;
Edge *e=V[u].head;
while(e!=NULL)
{
int v=e->end;
if(!(--V[v].indegree)) s.push(v);
e=e->next;
}
}
if(count<n) return false;
else return true;
}
void graph::DAGShortestPath(int s)
{
if(TopoSort())
{
int u,v;
for(u=1;u<=n;u++)
{
V[u].d=0x7ffffff;
V[u].Pie=0;
}
V[s].d=0;
while(!Q.empty())
{
u=Q.front();
Q.pop();
Edge *e=V[u].head;
while(e!=NULL)
{
v=e->end;
if(V[v].d>V[u].d+e->value)
{
V[v].d=V[u].d+e->value;
V[v].Pie=u;
}
e=e->next;
}
}
}
else cout<<"有回路"<<endl;
}
void graph::printPathLength(int s)
{
DAGShortestPath(s);
for(int i=1;i<=n;i++) cout<<V[i].d<<" ";
cout<<endl;
}
void graph::printPath(int s,int v)
{
if(s==v) cout<<s<<" ";
else
{
if(V[v].Pie==0)
{
cout<<"no path from "<<s<<" to "<<v;
return;
}
else
{
printPath(s,V[v].Pie);
cout<<v<<" ";
}
}
}
int main()
{
int n;
cout<<"输入图中顶点个数:"<<endl;
cin>>n;
graph G(n);
G.creatDirectedGraph();
G.printPathLength(1);
for(int i=1;i<=n;i++)
{
G.printPath(1,i);
cout<<endl;
}
return 0;
}
有向无环图的单源最短路径问题
最新推荐文章于 2021-02-13 06:21:34 发布