最近刷题需要学习很多算法知识,其中学到的迪杰斯特拉算法,为理解作此文:
该算法关键是不停的找最小值并且松弛,即对于每次找到的最小值 都要遍历这个最小值的邻居,判断(此时的最小值开销)和(这个最小值与他的邻居之间的开销), 这两个开销之和是否会小于(这个邻居上一次计算出的开销) ,如果小于就更新
这一段描述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;
}
制作不易,好心人点个赞吧(