回顾图经典Dijstra算法

本文详细探讨了Dijkstra算法的实现细节,包括初始化步骤、主循环逻辑及如何避免使用最大距离值。并通过代码示例展示了两种不同的实现方式。

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

今天回顾了经典的Dijstra算法,发现还是有些coding 地方没处理好,结果还弄了一会儿,这个算法按道理就是要一气呵成的,总结下遇到的问题。

MAXDIst不可避免,虽然这种设置会有缺陷,比如你不知道最大的距离会到多大,只能设置个看起来较大的数。。。

我已开始回想总是想不起来外层循环是干嘛。。。后来看了算法思想才知道,是和一般的循环有区别外层循环其实是逐个将除掉source以外的n-1个点加到一个集合里,这个集合表示所有已经算出最短距离的点击。因此出现了集合操作,但是教材的算法都是不断的去看visit变量来看是否加入,这种感觉挺别扭的,为啥原始不是涉及集合操作,集合的删除,插入等,从n-1个点击删除,插入到算出的点击。后来想想还有有原因的,因为要经常通过index来访问对应的dist,edge matrix,这些都是数组存储的,但是好像也可以啊,比如集合通过遍历访问,然后找到后如果可以获取他的index,那其实和不用集合没有本质区别。。。

注意题目是0-based 还是1-based index,这个一般不会有问题。

前面的初始化份三步,

1.先memset bool 数组 false,但原点为TRUE,因为一开始就加入点击了,距离不用定义,否则到后面可能误访问了原点的。

2.距离如果用-1表示的要先还原为MAXDIST,因为后面有个找min dist ,如果-1 当然设置if条件表达式也可以排除,后面dist update好像也是可以排除,这个可能可以出个考题,好吧自己再YY。。。

3.初始化dist为距离,除掉sourcei的 dist 其实这个赋值多少都不影响大局,因为后面根本不会访问到

主算法是外层一个for n-1次loop,因为加入了原点了。内层两次forloop,第一次算最小dist,包括index,因为后面

要把它加入集合,还有后面updatedist。然后后面for update dist。

貌似完成了,但是最需要当心的就是update的 时候 有一项出现了MinDIST 结果是否和预期的一样。还有就是找Min的时候

如果剩下的全是MAXDIST,怎么处理,所以这里我没考虑,于是出现了后面用Minj野值的情况,而且这里还发现了一个可以某些情况减少操作次数的case,

当remaining dist全都是MAXDIST时候,说明剩下的最短距离不会在更新了,因为找不到点可以到他们呢,也即他们分别是鼓励的点,所以可以直接break外层循环了,

不记得教材有没有这么做。

最后附上代码,好像可以优化成不需要MAXDIST的,待会儿试试

#include <iostream>
using namespace std;
#define MaxDist 100000
void dijstra(int **Edge, int*Dist, int n, int sourcei)
{
	//initilal bool
	bool *HasGotShortest=new bool[n];//不要用new,fawks大神说过了的,另外new也不用memset,因为堆的默认0
	memset(HasGotShortest,0,n*sizeof(bool));
	HasGotShortest[sourcei]=true;


	//update Edge as Max, if -1, because need to compare
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			if(Edge[i][j]==-1)
				Edge[i][j]=MaxDist;//这个还是有必要的,感觉后面的简直就是作死,完全没有这么做的。

	//initial dist
	for(int i=0;i<n;i++)
	{
		if(i!=sourcei)//不判断没事儿,因为Edge[sourcei][sourcei]=0,Edge如果是堆或者全局变量的默认也是0,再说Dist[sourcei]就算是野值也不影响,因为后面不会用到的,用visit把他挡开了
			Dist[i]=Edge[sourcei][i];
	}

	int min, minj;
	for(int i=1;i<n;i++)// add one vertex per loop, but not know which vertex这里一定是n-1轮,因为前面sourcei已经加入最短路径了,对应值为0
	{
		min=MaxDist;minj=-1;//循环变量初始化,index默认为-1吧
		for(int j=0;j<n;j++)//get reaminging sets' min dist, and minj
		{
			if(!HasGotShortest[j])
			{
				if(min>Dist[j])
				{
					min=Dist[j];
					minj=j;
				}
			}	
		}
		if(min==MaxDist)//remaining are all MaxDist, no add, not update,也可以判断minj==-1,总之true的话,后面都是不可达结点,不连通了,直接返回,否则访问visited数组后面会出错了,而不是提高效率的问题了
			break;
		
		HasGotShortest[minj]=true;//add new vertex, that is current remaining minimum dist

		for(int j=0;j<n;j++)//update  remaining sets' dist, according to just addde vertex
		{
			if(!HasGotShortest[j])
			{
				if(Dist[j]>Dist[minj]+Edge[minj][j])//Dist[minj]肯定不是MAX,另外两个分别是MAX和>0值的四种情况都是正确的
					Dist[j]=Dist[minj]+Edge[minj][j];
			}
		}
	}
	delete []HasGotShortest;
}
int main()
{
	int n;
	int casei=1;
	while(cin>>n)
	{
		int sourcei,desti;
		int **Edge=new int*[n];
		for(int i=0;i<n;i++)
			Edge[i]=new int[n];
		int *Dist=new int[n];
		for(int i=0;i<n;i++)
			for(int j=0;j<n;j++)
				cin>>Edge[i][j];
		cin>>sourcei>>desti;

		dijstra(Edge,Dist,n,sourcei-1);
		cout<<"Case "<<casei<<endl;
		casei++;
		cout<<Dist[desti-1]<<endl;

		delete []Dist;
		for(int i=0;i<n;i++)
			delete []Edge[i];
		delete[] Edge;
	}
	return 0;
}

果然经过调试,发现确实可以不用MaxDist,这样可以不用知道最大的距离为多少,只是找mindist,和update 的逻辑稍微纠结一点,但还是可以实现的。

void dijstra(int **Edge, int*Dist, int n, int sourcei)
{
	//initilal bool
	bool *HasGotShortest=new bool[n];
	memset(HasGotShortest,0,n*sizeof(bool));
	HasGotShortest[sourcei]=true;


	//update Edge as Max, if -1, because need to compare
	/*
	for(int i=0;i<n;i++)
		for(int j=0;j<n;j++)
			if(Edge[i][j]==-1)
				Edge[i][j]=MaxDist;
	*/

	//initial dist
	for(int i=0;i<n;i++)
	{
		if(i!=sourcei)
			Dist[i]=Edge[sourcei][i];
	}

	int min, minj;
	for(int i=1;i<n;i++)// add one vertex per loop, but not know which vertex
	{
		min=-1;bool firstdist=true;
		for(int j=0;j<n;j++)//get reaminging sets' min dist, and minj
		{
			if(!HasGotShortest[j])
			{
				if(Dist[j]>0&&(firstdist||min>Dist[j]))// if exsit Dist[j] >0, assign first positve dist to min
				{
					firstdist=false;
					min=Dist[j];
					minj=j;
				}
			}	
		}
		if(min==-1)//remaining are all MaxDist, no add, not update
			break;
		
		HasGotShortest[minj]=true;//add new vertex, that is current remaining minimum dist

		for(int j=0;j<n;j++)//update  remaining sets' dist, according to just addde vertex
		{
			if(!HasGotShortest[j])
			{
				if(Dist[j]==-1 && Edge[minj][j]!=-1)
				{
					Dist[j]=Dist[minj]+Edge[minj][j];
				}
				if(Dist[j]!=-1&&Edge[minj][j]!=-1)
				{
					if(Dist[j]>Dist[minj]+Edge[minj][j])
						Dist[j]=Dist[minj]+Edge[minj][j];
				}
				//remaining two case not update Dist[j]
			}
		}
	}
	delete []HasGotShortest;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值