狄克斯特拉算法的C++实现

本代码为个人学习存档,发布仅作为分享。欢迎各位大佬们指正优化,对于表述不当处敬请包涵

本人C++实力弱,语言和C语言可能会混合使用,文字篇幅长且啰嗦,适合实在看不懂大佬教程的人看看

/*
该算法关键是不停的找最小值并且松弛,即对于每次找到的最小值
都要遍历这个最小值的邻居,判断(此时的最小值开销)和(这个最小值与他的邻居之间的开销), 
这两个开销之和是否会小于(这个邻居上一次计算出的开销) ,如果小于就更新 
*/

//接下来进行详细说明 

/*
这一段描述main函数: 
程序的一开始会输入节点数和边数,接下来输入已知的节点间距离,将他存入二维数组(实现从图到数据的抽象) 
这个二维数组实际上已经通过节点数定义好了,两个参数分别是边的两个端点 
我们初始化为无穷大(当然我需要在函数前定义无穷大的概念),通过输入端点和边长,将部分无穷大更新 
其实我们这里是在模拟一开始节点间互不相连,通过输入进行相连,直到输入结束,图就成功抽象为数据
输入你要进行判定的起点,接下来调用函数,进入函数说明 
*/
 
//接下来是算法函数,直接上代码 

#include<bits/stdc++.h>
#define INF 100000000//定义无穷大 INF
using namespace std;
//定义函数狄克斯特拉算法,返回类型为数组 
vector<int> dkstl(vector<vector<int>>graph,int start){
	//接下来定义目前节点开销,父节点,以及确认是否被计算过(标记过) 的数组
	//实际上ischeck是bool类型,这里属于个人喜好 
 	vector<int>dict;
 	vector<int>dad;
 	vector<int>ischeck;
 	/*
	 其中dict就是对传入的二维数组,在下标为start时的一维数组,进行的照搬copy
	因为通过迭代器照搬一个个传入dict数组,所以迭代次数就是节点数
	因此我可以在每次迭代时,在dad数组和ischeck数组中插入值
	对于ischeck的初始化:还没check过的定义为1,一开始还没运行,大家都是1 
	对于父节点的初始化:如果对应dict为无穷大,说明在原二维数组中起点和该节点没有联系
	那这个节点就是没有父节点,假定为-1(其实也不对,只不过后面可以对他的父节点更新) 
	在这里我们默认父节点都是起点时dict有最小值,实际上不是,我们可以在后面进行更新 
 	*/ 
	for(auto p=graph[start].begin();p!=graph[start].end();p++){
 		dict.push_back(*p);
 		ischeck.push_back(1);
		if(*p!=INF){
			dad.push_back(start);
		}else{
			dad.push_back(-1);
		}
	}
	//为了方便,我们初始化起点的开销为0,起点不能再被检查(定义ischeck为0),以及起点的父节点就是他本身 
	dict[start]=0;
	ischeck[start]=0;
	dad[start]=start;
	//接下来进行dict.size()-1次操作,也就是节点数-1次操作,第一层for循环只用来记录循环次数,引入的j没有其他作用 
	for(int j=0;j<dict.size()-1;j++){
		//定义最大值 
		int minlen=INF;
		int d=start;
//		for(int i=0;i<dict.size()&&ischeck[i];i++){......} 
//		这是之前的错误,这么写会导致循环提前终止 ,判断是否被检查应该在循环内部
		for(int i=0;i<dict.size();i++){
			if(minlen>dict[i]&&ischeck[i]){
				//注意这个最小节点ischeck得是1,即还没被查到过 
				minlen=dict[i];
		 		d=i;
			}
		}
		//上述第二层for循环遍历dict,找到dict最小值,并用d记录最小值下标 
		ischeck[d]=0;
		//最小值找到了,让他标记为已经检查过(实际上还没完,得等下面走完,不过不影响)
		//这样在下一次进入第一层for循环找的就是第二小的值(因为会有ischeck判断)以此类推 
		if(d==start){
			continue;
			//这里说的是,我们一开始定义d是start,如果到现在还没找到比minlen=INF还小的值
			//说明在这个节点还没被查到过的其他节点和自己相距无穷
			//已经没有讨论意义的,因为这是个死胡同,通过上面把他的ischeck标记为0,直接进入下个循环 
		}else{
			//如果满足条件准备松弛
			for(int k=0;k<dict.size();k++){
				//如果从起点经过这个最小值的节点到另一个节点,能比从起点直接到那个节点快
				//我们就更新dick对应的值,同时记录他的父节点,实现dick不断缩小直到无法缩小,得到的就是最小值 
				if(dict[k]>minlen+graph[d][k]){
					dict[k]=minlen+graph[d][k];
					dad[k]=d;
				}
			}
		}
		//然后重复,找最小值,然后看能不能更新,直到把除节点外各个节点走个遍 
	}
	return dict;
 }
 
 int main(){
 	int n,m; 
 	cin >> n;
 	cin >> m;
 	int a,b,start;
 	vector<vector<int>>graph(n, vector<int>(n,INF)); 	
 	for(int i=0;i<m;i++){
 		cin >> a;
		cin >> b;
		cin >> graph[a-1][b-1]; 
		graph[b-1][a-1]=graph[a-1][b-1];
	}
	cin >> start;
	vector<int>END=dkstl(graph,start-1);
	//用最终的END数组接受返回值,迭代打印出来 
	for(auto p=END.begin();p!=END.end();p++){
		cout << *p << " ";
	}
 	return 0;
}

/*
疑问:dict里面一开始一定有的无穷大,就是我们假装认为,这个无穷大的节点和我们的起点不相连
有没有可能走完循环还是无穷大?
不可能,除非这个节点孤立出来,那就不符合图的规定了,各个节点可以间接相连
相连就一定有非无穷数值,这些数值之和一定比无穷大还要小,所以无穷大一定会被更新 
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值