c++ Dijkstra(迪杰斯特拉)代码

 单源最短路径

下图
S   表示已经找到最短路径
V-S 表示最短路径没有找到

 25b2fda295d047d987d506e3b738ec84.png

 下图
一开始345顶点属于V-S中,现在要连接,先连接3顶点
连接新顶点时,必须从S中选顶点,连接2到3,1-3最短路径是4
如果是链接1-3那么路径就是5,明显不是最短路径
这样一次链接
(迪杰斯特拉连路径怎么连可以看看王道考研数据结构视频,讲的比看文字强多了)

 76e9ef3f1959407e8597e8fd1ed92492.png

 

 9631dbf64c87481e807d2f124c7b9663.png

下图
重复执行的是n-1次,一共有n个点,除去源点1个,就剩n-1个,在剩下n-1个点里面循环找 

8d547b3cd5f34feb8c1ab0bf4d3144f8.png


 例子

下图
dist[]存储的是源点到各个顶点的路径,45没有与当前源点1相连所以距离为无穷
p[]存储的是顶点的前驱,这个前驱是源点,如果这个顶点前驱是源点p[i]=源点值,与前驱不是源点就p[i]=-1表示没有前驱
除了源点S,其他都是V-S集合
V-S中找最小,找到就加入到S集合,比如现在V-S中2顶点值最小,那就将顶点2加入S集合中,此时V-S就只剩345顶点,进行下一步

 0dad25785d0043988192454e2aea7078.png

 

下图
这也是松弛操作,3顶点能不能借1顶点或者借2顶点,发现借1顶点之后距离是5m,借2顶点距离为4,所以借2顶点
现在从顶点2开始链接V-S中的顶点,那就要更新V-S中的数据
dist[]存储的是源点到该点距离,现在源点链接了顶点2,那么距离有了一个2m,V-S中的到源点的距离就更新
顶点    3 4  5
距离源点 4 8 无穷
p[]中V-S的顶点前驱也要更改

那么现在dist[]里面的V-S中顶点3的距离距离源点最小为4m,把3顶点加入到S集合中,现在V-S剩余45两个结点

 17afae6969654c01ad4b91a08d54c341.png

 松弛操作

4顶点借3顶点或者2顶点
借3顶点 距离到源点就是4+7 = 11
借2顶点 距离到源点就是2+6 = 8
很明显8 < 11,所以不借3顶点,借用2顶点

5顶点借3或4
借3 距离4+1 = 5
借4 距离8+4 = 12

V-S剩余的顶点4 5距离源点距离分别为8 5
顺便改前驱
所以把5顶点加入S,V-S剩余4顶点

 6f1610dd7582423c8b11d382564775ef.png

 4顶点借5顶点,但是没有链接,不能借
所以4顶点只能不动,无法更新,所以没有领接点,什么也不用做,剩余的就是在V-S找最小值,现在V-S只有一个顶点4,距离源点为8m,将4顶点加入S
现在所有顶点都加入了S

331335fc53d346c6824d3f8bb8b33ceb.png

 比如找4顶点到源点1最短路径,那就是路径1——>2——>4,就是4的前驱是2,2的前驱是1,只有这几个前驱时,路径才最短
注意最短路径是那几个顶点不是正向,是逆向的1——>2——>4,这个是输出时候输出正向,但是在代码最后是4 2 1这个顺序,是输出时候逆序输出方便观看

c3d09ea6bf5448fa893eab08fc4a373d.png

下面是第一个代码的测试数据

第一行是顶点个数(5)和边数(8)

剩余8行是每条边两段顶点和边的权

5 8
1 2 2
1 3 5
2 3 2
2 4 6
3 4 7
3 5 1
4 3 2
4 5 4
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int N = 1005;//顶点(结点)数 
const int INF = 0x3f3f;//无穷大,领接矩阵初始值 
const int inf = 0x3f3f3f;
int G[N][N];//领接矩阵,保存图 
int dist[N]; //dist[i]表示源点到结点i的最短路径
int p[N];//p[i]表示源点到顶点i的最短路径上的i的前驱
int n,m;//n为顶点数,m为边数 
bool flag[N];// 如果flag[i]为true,说明顶点i加入S集合,否则i属于V-S集合
int sum=0;//最短路径长度 
//初始化领接矩阵
void init(){
	cin >>n >> m;
	int u,v,w;
	for(int i = 1 ; i <= n ; i++){
		for(int j = 1 ; j <= n ; j ++){
			G[i][j] = INF;
		}
	}
	for(int i = 1 ; i <= m ; i ++){
		cin >> u >> v >> w;
		G[u][v] = w;
	}
	for(int i = 1 ; i <= n ; i ++){
		for(int j = 1 ; j <= n ; j++){
			cout<<G[i][j]<<"	";
		}
		cout<<endl;
	}
}

void djtsl(int u){//u是源点,求的是源点到其他顶点最短路径
	//初始化 
	for(int i = 1 ; i <= n ; i++){
		//dist[]表示的是源点到各个顶点的最短路径,注意是从源点!比如源点是1,现在dist存储的就是1到各个顶点的距离 
		//领接矩阵中直接1顶点到1顶点距离是0 
		dist[i] = G[u][i];//将源点到各个顶点i的距离读入dist[i] 
		flag[i] = false;//初始化,还没有一个顶点加入S 
		if(dist[i] == INF){ //==INF就是不与源点连接,dist[i]值是从领接矩阵获取的 
			p[i] = -1;//不邻接说明前驱不是源点,那么前驱赋值为-1 
		} else{
			p[i] = u;//连接的话前驱就是u 
		} 	
	}
	dist[u] = 0;//自己到自己是0 
	flag[u] = true;//u加入S集合
	
	// 找最小 - 松弛 循环n-1次 
	for(int i = 1 ; i <= n-1 ; i++ ){
		int temp = inf;
		int t = u;//加入S,这个初始为u是一会拿来判断的 
		//找最小 在 V-S集合中 
		for(int j = 1 ;j <= n ; j++){
			//由于不知道谁是V-S集合,先判断,在V-S找最小放入S集合 
			 if(!flag[j] && dist[j] < temp){//是V-S集合 并且 dist[j]小于当前就更改 
			 	temp = dist[j];
				 t = j;//记录顶点,一会放入S集合 
			 } 
		}
		if(t == u) return;//如果还是t==u说明上面找最小就没有找到,直接返回就可以
		flag[t] = true;//找到就加入S  
		//松弛操作 判断V-S集合的顶点是否借助t松弛dist[] 
		for(int j = 1 ; j <= n ; j++){
			//如果借助t顶点比之前距离段就松弛更新 
            //dist[t]+G[t][j]这是源点到t再+t到j距离 
			if(!flag[j] && dist[t]+G[t][j] <dist[j] ){
				dist[j] = dist[t]+G[t][j];
				p[j] = t;//因为借助t,所以前驱更改 
				 
			} 
		}
	} 
}
//递归输出源点到终点的路径 
void fin(int x){ //这个x是终点不是源点 
	if(x == -1) return ;
	fin(p[x]);
	cout<<x<<" ";
	
}
int main(){
	//函数的里面的参数自己可以再设置变量传入,本人为了省事就直接写参数 
	init();
	cout<<"-------------------"<<endl;
	djtsl(1);
	cout<<"--------------------"<<endl;
	 
	//这是输入终点,假设终点为顶点5 
	fin(5);
	cout<<"--------------------"<<endl;
	//输出源点到终点顶点5的最短路径 
	cout<<dist[5];
}

相对简便的

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 510;

int g[N][N], dist[N];
int n, m;
bool st[N];

int dijkstra(){
    memset(dist, 0x3f, sizeof dist);
    dist[1] = 0;
    
    for (int i = 0; i < n; i ++){
        int t = -1;
        for (int j = 1; j <= n; j ++)
            if (!st[j] && (t == -1 || dist[t] > dist[j]))
                t = j;
                
        for (int j = 1; j <= n; j ++)
            dist[j] = min(dist[j], dist[t] + g[t][j]);
            
        st[t] = true;
    }
    
    
    if (dist[n] == 0x3f3f3f3f) return -1;
    return dist[n];
}

int main(){
    memset(g, 0x3f, sizeof g);
    cin >> n >> m;
    
    while (m --){
        int a, b, c;
        cin >> a >> b >> c;
        g[a][b] = min(g[a][b], c);
    }
    
    cout << dijkstra() << endl;
    
    return 0;
}

第二个代码的测试数据

输入

3 3
1 2 2
2 3 1
1 3 4

输出最短路径 3 

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值