本代码为个人学习存档,发布仅作为分享。欢迎各位大佬们指正优化,对于表述不当处敬请包涵
本人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里面一开始一定有的无穷大,就是我们假装认为,这个无穷大的节点和我们的起点不相连
有没有可能走完循环还是无穷大?
不可能,除非这个节点孤立出来,那就不符合图的规定了,各个节点可以间接相连
相连就一定有非无穷数值,这些数值之和一定比无穷大还要小,所以无穷大一定会被更新
*/