提示:醉后不知天在水,满船清梦压星河
注意
为什么这里需要初始化邻接矩阵的时候不能是0,将0修改成Maxinf呢?如果两点没有连接或是同一个节点,可以用0或正无穷表示。但是在计算该算法时,如果用零的话,计算过程肯定认为 0 是最短路径,这样我们最终得到的最短路径距离矩阵就是全零矩阵了。所以一定要用Maxinf来表示没有连接的两个节点
(或者就是在使用值的时候,加上一个判断值的语句)。
不能为负权
迪杰斯特拉求的是不带负权值的单源最短路径,至于为什么?
Dijkstra算法当中将节点分为已求得最短路径的集合(记为V)和未确定最短路径的个集合(记为S),
归入V集合的节点的最短路径及其长度不再变更,如果边上的权值允许为负值,那么有可能出现当与V
内某点(记为a)以负边相连的点(记为b)确定其最短路径时,它的最短路径长度加上这条负边的权值
结果小于a原先确定的最短路径长度,而此时a在Dijkstra算法下是无法更新的,由此便可能得不到正确的结果。
求带负权值边的单源最短路径可以用贝尔曼-福特算法。
比如下面这个图,第一次选自然是选的AB 也就定下来dict[b]=1 但是若是选择ACEB 值就会是0 所以不能使用迪杰斯特拉
两个数组
许多人不懂这个算法 我想许多是因为不懂这三个数组表的具体含义
distance[i] :记录i顶点到源点的距离
visited[i]::记录i顶点是否访问过 若是访问过则设置为ture
从所有未被访问过的结点中寻找一个最小的distance[k]
visited[k]=ture;
遍历所有与k相连的点j 使用min(distance[j],ditsace[k]+edge[k][j])来更新distance[j]
ps: 其实知道这些也就可以写出代码了,但是还有一种优化方式,就是再加一个数组 用来存储结点前驱结点。
实现原理
使用的是贪心的思想,即保证每一个【被保存的路径】都是通过【前一个路径】得出的,每条路径都是最短的,得出的路径必然是最短的,最开始的时候是一个点的时候最优,然后两个点的时候最优,直到所有的点到达源点的解最优
为什么可以这样实现,visited[]数组将点分为一个是遍历过的点集的集合V 一个是未遍历的点集S ,源点到达S的最短距离被存储在distance[]中,每加入一个点 就将distace更新 此时的distace[] 就是在以知条件下源点与S最短距离,所以下一次直接取 ,然后再更新distance[],或者逆向来看,第一次表示源结点到其他结点的距离,第二次我们要选择一个S中的点作为要加入的点,此时加入那个呢?,自然是距离源点最短的 也就是从dict中选取一个最小值, 这个点加入V之后,那么加入之后的源点与S的距离是否发生了改变? 所以也就需要更新dict[]
手写计算
迪杰斯特拉代码
void Djstl(AMGraph &G){
int dict[G.vexnum];//用来存储最短距离
bool visited[G.vexnum];//用来判断是否已经是最优解
int pre[MVNum];//用来存储当前最优结点的前驱的下标
//因为之前若是没有边 放的是0 这里将0 修改成MaxInt
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(0==G.arcs[i][j]){
G.arcs[i][j]=MaxInt;
}
}
}
//初始化
for(int i=0;i<G.vexnum;i++){
visited[i]=false;
dict[i]=G.arcs[0][i];//初始化dict[]
}
//将源点加入到最优点中
visited[0]=true;dict[0]=0;int i=0;
cout<<"此时的dict数组是"<<endl;
for(int i=0;i<G.vexnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
for(int h=1;h<G.vexnum;h++){
//来寻找此时的dict中最小的值 并记录下来最小值对应的结点的位置
int Min=MaxInt;int k=0;
for(int i=0;i<G.vexnum;i++){//从dict中寻找到最小值
if(visited[i]==false&&dict[i]<Min){
Min=dict[i];
k=i;//找到这个位置是为了遍历与此节点相邻的边
}
}
visited[k]=true;//给k对应的结果放在结果集中
for(int j=0;j<G.vexnum;j++){//来更新dict[]数组
if(visited[j]==false&&dict[j]>dict[k]+G.arcs[k][j]){
dict[j]=dict[k]+G.arcs[k][j];
}
}
cout<<"此时的dict数组是"<<endl;
for(int i=0;i<G.vexnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
}
}
运行截图
运行截图也就和之前我们手动分析的一样
可执行代码
#include<bits/stdc++.h>
using namespace std;
#define MaxInt 32767 //表示极大值∞ 其实就是一种无穷标志
#define MVNum 100 //表示最大顶点数
typedef char VerTexType;//假设顶点的数据结构类型为char
typedef int ArcType;//假设权值类型为整形
//下面的是邻接矩阵的
typedef struct{
VerTexType vexs[MVNum];//顶点表
ArcType arcs[MVNum][MVNum];//邻接矩阵
int vexnum;//图的当前顶点数
int arcnum;//图的当前边数
}AMGraph;
//打印邻接矩阵
void PrintAMG(AMGraph G){
cout<<" "<<" ";
for(int i=0;i<G.vexnum;i++){
cout<<G.vexs[i]<<" ";
}
cout<<endl;
for(int i=0;i<G.vexnum;i++){
cout<<G.vexs[i]<<" ";
for(int j=0;j<G.vexnum;j++){
cout<<G.arcs[i][j]<<" ";
}
cout<<endl;
}
}
//创建一个无向图 ,其实就是填充两个数组
void CreateAdjoin(AMGraph &G){
cout<<"请输入顶点数和边数"<<endl;
cin>>G.vexnum>>G.arcnum;
cout<<"请输入各个顶点名称"<<endl;
for(int i=0;i<G.vexnum;i++){
cin>>G.vexs[i];
}
for(int h=0;h<G.arcnum;h++){
char vex1;char vex2;
cout<<"请输入弧的两个顶点"<<endl;
cin>>vex1>>vex2;
//首先来看是否存在这两个顶点 若是存在则找到这两个点对应的下标
// 当然创建的时候一种更加简单的方式就是遍历二维数组 一个一个输入
int i=0;while(G.vexs[i++]!=vex1);
int j=0;while(G.vexs[j++]!=vex2);
if(i>G.vexnum||j>G.vexnum){//没有找到
cout<<"你输入的结点值不正确"<<endl;
}
else{
cout<<"请输入此边对应的权重"<<endl;
cin>>G.arcs[i-1][j-1];
G.arcs[j-1][i-1]=G.arcs[i-1][j-1];
}
}
PrintAMG(G);
}
/**/
void Djstl(AMGraph &G){
int dict[G.vexnum];//用来存储最短距离
bool visited[G.vexnum];//用来判断是否已经是最优解
int pre[MVNum];//用来存储当前最优结点的前驱的下标
//因为之前若是没有边 放的是0 这里将0 修改成MaxInt
for(int i=0;i<G.vexnum;i++){
for(int j=0;j<G.vexnum;j++){
if(0==G.arcs[i][j]){
G.arcs[i][j]=MaxInt;
}
}
}
//初始化
for(int i=0;i<G.vexnum;i++){
visited[i]=false;
dict[i]=G.arcs[0][i];//初始化dict[]
}
//将源点加入到最优点中
visited[0]=true;dict[0]=0;int i=0;
cout<<"此时的dict数组是"<<endl;
for(int i=0;i<G.vexnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
for(int h=1;h<G.vexnum;h++){
//来寻找此时的dict中最小的值 并记录下来最小值对应的结点的位置
int Min=MaxInt;int k=0;
for(int i=0;i<G.vexnum;i++){//从dict中寻找到最小值
if(visited[i]==false&&dict[i]<Min){
Min=dict[i];
k=i;//找到这个位置是为了遍历与此节点相邻的边
}
}
visited[k]=true;//给k对应的结果放在结果集中
for(int j=0;j<G.vexnum;j++){//来更新dict[]数组
if(visited[j]==false&&dict[j]>dict[k]+G.arcs[k][j]){
dict[j]=dict[k]+G.arcs[k][j];
}
}
cout<<"此时的dict数组是"<<endl;
for(int i=0;i<G.vexnum;i++){
cout<<dict[i]<<" ";
}
cout<<endl;
}
}
int main(){
AMGraph G;
CreateAdjoin(G);
Djstl(G);
}
总结
就迪杰斯特拉写代码写起来是简单的 但是若是真正理解还是有难度的 感觉不错点个赞呗