机试算法讲解: 第37题 Dijkstra

/*
Dijkstra:单源最短路径。第m+1近的结点与结点1的最短路径上的中间节点一定属于集合K,任意最短路径中间有一个不属于集合K的节点,则它的最短路径距离一定<第
m+1近的节点的最短路径长度,与距离与第m+1近的节点的最短路径已经确定、这样的结点属于集合K相矛盾。
最短路径 = 从结点1出发经最短路径集合中K中的节点P2 +  <p2,Vn>

算法流程:
1)初始化,在集合K中加入结点1,结点1到结点1的最短距离为0,到其他结点距离为无穷大
2)遍历与集合K中结点直接相邻的边(U,V,C),其中U属于集合K,V不属于集合K,计算从结点1出发,按照已经得到的最短路径到达U,再由U经过该边到达V的路径长度。比较所有与集合K中直接相邻的非集合K中结点的
路径长度,路径长度最小的节点为下一个确定节点,将该结点加入集合K
3)若集合K中含有所有节点,结束

先更新距离,后挑选路径最短的节点加入,对于已经加入的节点需要直接跳过,通过vector来构建单链表
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#define MAX 100
using namespace std;

typedef struct Edge
{
	int _iNext;//下一个节点编号
	int _iDistance;//从当前节点到下一个节点之间的距离
}Edge;

bool Mark[MAX];//标记是否已经是在已知节点集合
int Dist[MAX];//距离向量,为true表示最短记录。否则,表示经过中间节点的最短距离

int main(int argc)
{
	int n,m,i,j,iVi,iVj,iDis;
	vector<Edge> vecEdge[MAX];//用了100个数组,每个数组用来存放单链表信息
	while(EOF!=scanf("%d %d",&n,&m))
	{
		//易错,n和m都为0的时候要退出
		if(0==n && 0==m)
		{
			break;
		}

		//易错,要初始化邻接链表
		for(i = 1 ; i <=n ; i++)
		{
			vecEdge[i].clear();
		}

		//获取每条边的信息
		for(i = 0 ; i < m ; i++)
		{
			scanf("%d %d %d",&iVi,&iVj,&iDis);
			//要构造一个结构体
			Edge edge,edge2;
			edge._iNext = iVj;
			edge._iDistance = iDis;
			edge2._iNext = iVi;
			edge2._iDistance = iDis;
			//由于是无向图,所以需要添加2条边
			vecEdge[iVi].push_back(edge);
			vecEdge[iVj].push_back(edge2);
		}

		//初始化
		for(int i = 1 ; i < MAX; i++)
		{
			Mark[i] = false;
			Dist[i] = -1;
		}
		Dist[1] = 0;//设置节点1的长度为0
		Mark[1] = true;//设置节点1为已经加入集合
		//易错,需要设置一个新节点
		int iNewP = 1;

		//遍历n-1趟,先更新距离,再挑选最短距离
		for(i = 1 ; i < n ; i++)
		{
			for(j = 0 ; j < vecEdge[iNewP].size(); j++)
			//for(j = 1 ; j < vecEdge[iNewP].size() ; j++)//遍历vector必须从0开始遍历
			{
				//取出边,判断与其相连的节点是否已经被标记
				int iNext = vecEdge[iNewP][j]._iNext;
				int iWeight = vecEdge[iNewP][j]._iDistance;
				//如果已经属于集合K,则跳过
				//if(iNext==true)
				if(Mark[iNext]==true)
				{
					continue;
				}
				//如果距离不可达 或者走新加入节点的路径距离更短时,更新距离
				if(Dist[iNext]==-1 || Dist[iNewP] + iWeight < Dist[iNext])
				{
					Dist[iNext] = Dist[iNewP] + iWeight;
				}
			}

			//选择距离最短的结点,遍历所有节点
			int min = 123123123;
			for(j = 1 ; j <= n;j++)
			{
				//若其属于集合K,节点不可达,则跳过
				if(Mark[j]==true || -1==Dist[j])
				{
					continue;
				}
				if(Dist[j] < min)
				{
					min = Dist[j];
					iNewP = j;//更新加入的结点
				}
			}
			//设置新加入的节点标记
			Mark[iNewP] = true;
		}
		printf("%d\n",Dist[n]);

	}
	system("pause");
	getchar();
	return 0;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值