有向无环图的单源最短路径问题

本文介绍了如何利用有向无环图(DAG)进行单源最短路径的求解。通过定义边结构体、顶点结构体以及图类,实现了添加边、拓扑排序、计算入度以及单源最短路径等功能。最后展示了通过输入顶点个数和邻接表创建有向图,并输出从源点到所有其他顶点的最短路径长度和具体路径。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

#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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值