数据结构——最短路径之Dijkstra算法(与最小生成树的prime算法很像,建议一起看)

本文介绍了Dijkstra算法,用于解决单源最短路径问题,特别是在非负权值边的情况下。算法思想包括初始化邻接矩阵,通过不断找到未访问且距离源点最近的顶点进行路径更新。文章通过与Prime算法的对比,帮助读者理解Dijkstra算法的工作原理,并提供了AcWing 849题目的实例和朴素实现。

最短路径之Dijkstra算法
(一)Dijkstra算法

单源最短路径:就是从某一个顶点出发,到图中任意顶点之间的最短路径;

【算法概述】:Dijkstra算法适用于解决单源最短路径的问题。即:从源点到任意指定顶点之间的最短距离的问题;但Dijkstra算法要求所有边的权值非负。看过Prime算法的同学都知道,Dijkstra算法与Prime算法很相似,不同的就是dis数组的更新方式。Dijkstra算法用邻接矩阵存图比较方便。

【算法思想】:先用一个数组记录从源点到图中个顶点直接相连的距离,如果不直接连,就记录为无穷大,然后通过对该数组的更新,使得dis[x]表示从源点到x的最短路径;

1.1 初始化

用邻接矩阵来存图,先进行初始化,自己到自己的距离初始化为0,到另外的顶点的距离初始化为无穷大。并将标记数组都置为0,表示所有顶点都未访问;

void init(){
	for(int i=0;i<nodeNum;i++){
		for(int j=0;j<nodeNum;j++){
			if(i==j)
				matrix[i][j]=0;
			else
				matrix[i][j]=INF;
		} 
	}
	memset(visited,0,sizeof(visited));	
}

1.2 Dijkstra主体
参数st表示源点,此题是0
第一步:先用min_distance数组储存与0直接相连的顶点之间的距离。并标记0号顶点;然后用一个while死循环来变量所有顶点,最坏的情况就是遍历所有顶点。
第二步:就是在当前的min_distance数组里找一个顶点没有访问过且距离源点最近的点,记录它的顶点下标;
第三步:更新min_distance数组:如果x顶点没有被访问,并且0到x顶点的距离(直接或间接)大于上面min_distance数组最小值加上点p到x点的距离,就对min_distance数组更新: min_distance[x] = min + matrix[p][x];(可以理解为:从源点直接到某点的距离大于间接到某点的距离
第四步:重复上面的第二步和第三步;
图示:
第一步:此时的dis数组:0 5 2 7;并标记源点0;此时的vis数组:1 0 0 0

void Dijkstra(int start){
	//第一个结点进入,标记已走过 
	for(int i=0;i<nodeNum;i++){
		min_distance[i]=matrix[start][i];
	}
	/*for(int i=0;i<nodeNum;i++){
		cout<<min_distance[i];
	}*/
	visited[start]=true;//表示start结点已经走过
	
	
	
	
	while(true){
		//找到距离这个结点最近的节点的坐标 
		int min=INF;
		int index=-1;
		for(int i=0;i<nodeNum;i++){
			if(visited[i]==false && min_distance[i]<min){
				min=min_distance[i];//从起点到index的最小距离 
				index=i;
			}
		}
		visited[index]=true;//标记已经访问
		if(index==-1)//此时所有的节点都已经访问过了 
			break;
		for(int i=0;i<nodeNum;i++){//添加index结点之后更新到达还未访问的点的路径长度 
			if(visited[i]==false && matrix[index][i]+min<min_distance[i]){
				min_distance[i]=matrix[index][i]+min;
			}
		} 
		 
		
		
	} 
	
}
 

prime算法与迪杰斯特拉算法的比较很相似
仅在min_distance数组的变化上不同
迪杰斯特拉算法:

for(int i=0;i<nodeNum;i++){//添加index结点之后更新到达还未访问的点的路径长度 
			if(visited[i]==false && matrix[index][i]+min<min_distance[i]){
				min_distance[i]=matrix[index][i]+min;
			}
		} 


if(visited[i]==false&&matrix[index][i]<min_distance[i]){
					//当加入新结点(index)之后,达到c(另外一个结点)的距离变小了
					min_distance[i]=matrix[index][i];
 					father[i]=index;
					} 

全部代码:

#include<iostream>
#include<cstring>
using namespace std;
#define MAX 1000
#define INF 0x3f3f3f3f

/*输入:
4 6
0 1 5
0 2 2
0 3 7
1 3 1
1 2 6
2 3 2
0 3
输出:
0到3的最短路径为:4
vis数组:1 1 1 1
dis数组:0 5 2 4*/

int matrix[MAX][MAX];
bool visited[MAX];
int min_distance[MAX];
int nodeNum;//顶点的数量
int edgeNum;//边的数量 
void init(){
	for(int i=0;i<nodeNum;i++){
		for(int j=0;j<nodeNum;j++){
			if(i==j)
				matrix[i][j]=0;
			else
				matrix[i][j]=INF;
		} 
	}
	memset(visited,0,sizeof(visited));	
}

void Dijkstra(int start){
	//第一个结点进入,标记已走过 
	for(int i=0;i<nodeNum;i++){
		min_distance[i]=matrix[start][i];
	}
	/*for(int i=0;i<nodeNum;i++){
		cout<<min_distance[i];
	}*/
	visited[start]=true;//表示start结点已经走过
	
	
	
	
	while(true){
		//找到距离这个结点最近的节点的坐标 
		int min=INF;
		int index=-1;
		for(int i=0;i<nodeNum;i++){
			if(visited[i]==false && min_distance[i]<min){
				min=min_distance[i];//从起点到index的最小距离 
				index=i;
			}
		}
		visited[index]=true;//标记已经访问
		if(index==-1)//此时所有的节点都已经访问过了 
			break;
		for(int i=0;i<nodeNum;i++){//添加index结点之后更新到达还未访问的点的路径长度 
			if(visited[i]==false && matrix[index][i]+min<min_distance[i]){
				min_distance[i]=matrix[index][i]+min;
			}
		} 
		 
		
		
	} 
	
}
 



int main(){
	cin>>nodeNum>>edgeNum;
	int tempx,tempy,cost;
	init();
	for(int i=0;i<edgeNum;i++){
		cin>>tempx>>tempy>>cost;
		matrix[tempx][tempy]=cost;
		matrix[tempy][tempx]=cost;
	}
//矩阵的存储没问题
	for(int i=0;i<nodeNum;i++){
		for(int j=0;j<nodeNum;j++){
			
				cout<<matrix[i][j]<<" ";
		} 
		cout<<endl;
	}
 	Dijkstra(0);
 	cout<<endl<<"min_distance数组:";
 	for(int i=0;i<nodeNum;i++){
 		cout<<min_distance[i]<<" "; 
	 }
	 cout<<endl<<"visited数组:";
 	for(int i=0;i<nodeNum;i++){
 		cout<<visited[i]<<" "; 
	 }
	
} 

测试数据:

4 6
0 1 5
0 2 2
0 3 7
1 3 1
1 2 6
2 3 2

在这里插入图片描述

AcWing 849. Dijkstra求最短路 I

朴素dijkstra算法 :AcWing 849. Dijkstra求最短路 I
在这里插入图片描述
在这里插入图片描述

朴素写法

#include<bits/stdc++.h>
using namespace std;
const int N=510;
const int INF=0x3f3f3f3f;
int g[N][N];
int dist[N];//dist[i]:从起点到i结点的最少花费 
bool st[N];//true:走过   false:没走 

int n,m;


void dijk()
{
	memset(dist,INF,sizeof(dist));
	dist[1]=0;
	
	for(int i=1;i<=n;i++)
	{
		//纳入新结点 
		int index=-1;
		for(int j=1;j<=n;j++)
		{
			if(!st[j]&&(index==-1||dist[index]>dist[j]))
				index=j;
		}
		st[index]=true;
		
		
		//时事更新 
		for(int j=1;j<=n;j++)
		{
			dist[j]=min(dist[j],dist[index]+g[index][j]);
		}
	 } 
	
}
int main()
{
	cin>>n>>m;
	//初始化 
	memset(g,INF,sizeof(g));//邻接矩阵的初始化,由于求的是最小值,因此初始为无穷大
	while(m--)
	{
		int a,b,c;
		cin>>a>>b>>c;
		g[a][b]=min(g[a][b],c);//如有重边,则取最小值
	}
	dijk();
	if(dist[n]==INF)
		cout<<"-1"<<endl; 
	else
		cout<<dist[n]<<endl;
	return 0;
 } 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值