【数据结构学习笔记】10:三个最短路径算法注解

Dijkstra

①迪杰斯特拉算法:用于求弧上权值非负的单源点最短路径问题。用开辟好的数组dist[]和path[]分别记录到未完成点的当前最短和当前前驱。在循环中做三件事:①查:查找未完成点中哪个的路径最短,即为本次循环找出的最短路径,找这个路径终点u;②杀:将这个点u的tag域设为已访问,即到这个点的最短路径已经找出了;③改:对剩下的所有未完成点v,看这个刚完成的点u到它们的直接长度+原点到u的最短路径长(刚找到的minVal)是否比dist[v]即当前到v的路径长还短,如果是,刷新当前路径dist[v]为这个加和,刷新前驱path[v]为u。

template<class ElemType,class WeightType>
void Dijkstra(const NetWork<ElemType,WeightType> &g,int v0,int *path,WeightType *dist)//从v0到其它
{
	WeightType minVal;
	WeightType infinity=g.GetInfinity();//无穷大
	int v,u;

	/*初始化:path(当前前驱),dist(当前最短),访问情况*/
	for(v=0;v<g.GetVexNum();v++)//对每个顶点v
	{
		dist[v]=g.GetWeight(v0,v);//dist[v]的值初始为v0->v的权
		if(dist[v]==infinity)//如果v0->v没有
			path[v]=-1;//即没有前驱
		else
			path[v]=v0;//否则前驱为v0
		g.SetTag(v,UNVISITED);//每个顶点的tag都为未访问,表示到v的路径没找到
	}
	g.SetTag(v0,VISITED);//v0->v0不需要找,设置为已访问

	/*核心算法*/
	for(int i=1;i<g.GetVexNum();i++)//对除了v0的后面的n-1个点(i仅用于控制循环次数)
	{
        minVal=infinity;//最小边从无穷开始,保证能正确刷新
        u=v0;//最小路径对应终点索引从v0开始,保证能正确刷新
        /*查*/
        for(v=0;v<g.GetVexNum();v++)//对每个点v
            if(g.GetTag(v)==UNVISITED && dist[v]<minVal)//如果到v的路径还没找到,且比minVal更小
            {
                u=v;//更新最小路径对应终点的索引
                minVal=dist[v];//同步更新最小
            }
        /*杀*/
        g.SetTag(u,VISITED);//找到的最短路径对应终点u加入集中,相当于找到了到u的最短路径
        /*改*/
        for(v=g.FirstAdjVex(u);v!=-1;v=g.NextAdjVex(u,v))//对u的所有邻接点
            if(g.GetTag(v)==UNVISITED && minVal+g.GetWeight(u,v)<disk[v])//刚找到的到u的最短路径长+从u到v的直接路径长<当前到v的当前路径长
            {
                dist[v]=minVal+g.GetWeight(u,v);//更新到v的当前路径长
                path[v]=u;//更新当前前驱为u
            }
	}
}

时间复杂度:O(n^2)

BellmanFord

②贝尔曼-福特算法:用来求弧上权值为任意值的单源点最短路径问题。同样用开辟好的数组dist[]和path[]分别记录当前最短和当前前驱。这里的当前最短是不分完成和未完成状态的,而是每次对每个不为起始点的点,尝试在它前面插入一个和自己不同的点,看看这样得到的路径有没有变得更短,如果有就更新前驱、更新路径长,如此反复n-2次(最多插入非v0非自身的n-2个点),就得到了最短路径。

注意该方法要求图中不能有路径长度为负值的回路。

template<class ElemType,class WeightType>
void BellmanFord(const NetWork<ElemType,WeightType> &g,int v0,int *path,WeightType *dist)
{
	WeightType minVal;
	WeightType infinity=g.GetInfinity();//无穷
	int v,u,vexNum=g.GetVexNum();
	WeightType *distTemp=new WeightType[vexNum];//辅助数组,用前面的dist来生成它,再刷新回去
	
	/*初始化*/
	for(v=0;v<vexNum;v++)//对于每个顶点
	{
		dist[v]=(v0!=v)?g.GetWeight(v0,v):0;//存从v0到v的路径长
		if(dist[v]==infinity)//如果这个长度为无穷,也就是没有v0->v的边
			path[v]=-1;//v就没有前驱
		else
			path[v]=v0;//否则前驱就是v0
	}

	/*核心算法*/
	for(int k=2;k<vexNum;k++)//初始化得到的是经过1条边的,最多经过n-1条边,故迭代n-2次
	{
		for(v=0;v<vexNum;v++)//先将当前情况复制到辅助数组里
			distTemp[v]=dist[v];
		for(u=0;u<vexNum;u++)//对每个顶点u(到达u)
			if(u!=v0)//只要不是起始点v0
				for(v=0;v<vexNum;v++)//再次对每个顶点v(到达v及v直达u)
					if(v!=v0 && distTemp[u]>dist[v]+g.GetWeight(v,u))//两点不相同且到v当前长+v->u长度比到u当前长更短
					{
						distTemp[u]=dist[v]+g.GetWeight(v,u);//更新到u的路径长
						path[u]=v;//同步更新到u的前一个节点为v
					}
		for(v=0;v<vexNum;v++)
			dist[v]=distTemp[v];//将更新得到的结果放回来
	}
}

时间复杂度:用邻接矩阵时为O(n^3),用逆邻接表时为O(n^2+n*e)

Floyd

③弗洛伊德算法:该算法用于求所有顶点之间的最短路径,注意该算法不能视为广义的贝尔曼-福特算法,只是解决的问题是它的广义问题。用开辟好的数组dist[][]和path[][]分别记录当前最短和当前前驱。每次对于所有不同的i->j,看看尝试插入一个点(游标0,1,2..随循环增加)后,i->游标点+游标点->j有没有比当前的i->j更短,如果有就更新dist[i][j]中的权i->j,并将path[i][j]更新为path[游标点][j]。如此反复循环将游标为0,1,2,...,n-1的所有点都尝试过后,得到的最终的两个二维数组也就反映了所有顶点之间的最短路径。

template<class ElemType,class WeightType>
void Floyd(const NetWork<ElemType,WeightType> &g,int **path,Weight **dist)
{
	/*初始化*/
	for(int u=0;u<g.GetVexNum();u++)//对每一行
		for(int v=0;v<g.GetVexNum();v++)//对每一行中的每一列
		{
			dist[u][v]=(u!=v)?g.GetWeight(u,v):0;//初始路径长就是直达长度(权)
			if(u!=v && dist[u][v]<g.GetInfinity())//起点终点不同且有直达时
				path[u][v]=u;//u->v直达则前驱就是u
			else
				path[u][v]=-1;//否则不存在前驱游标
		}
	
	/*核心算法*/
	for(int k=0;k<g.GetVexNum();k++)//最外层循环,控制插入哪个点(十字坐标系的移动)
		for(int i=0;i<g.GetVexNum();i++)//对每一行
			for(int j=0;j<g.GetVexNum();j++)//对这行的每一列
				if(dist[i][k]+dist[k][j]<dist[i][j])//如果从i到k再到j比之前的i到j更短
				{
					dist[i][j]=dist[i][k]+dist[k][j];//更新这个"更短"的路径长
					path[i][j]=path[k][j];//同步更新i->j的前驱就是k->j的前驱
				}
}

时间复杂度:O(n^3)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值