迪杰斯特拉

提示:醉后不知天在水,满船清梦压星河


注意

为什么这里需要初始化邻接矩阵的时候不能是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); 
}

总结

就迪杰斯特拉写代码写起来是简单的 但是若是真正理解还是有难度的 感觉不错点个赞呗

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值