[C Language] Algorithm Learning · Dijkstra Algorithm Explanation

Table of contents

Dijkstra Algorithm Design

Introduction to Dijkstra's Algorithm

The basic idea of ​​Dijkstra algorithm

Dijkstra Greedy Strategy

perfect illustration

Detailed Pseudocode

full code

Algorithm analysis and optimization expansion

​Complete code using priority queue


Dijkstra Algorithm Design

Introduction to Dijkstra's Algorithm

        Dijkstra's algorithm is a **greedy algorithm** that solves the **single-source shortest path** problem.
        It first finds a path with the shortest length, and then refers to the shortest path to find a path with the second shortest length until it finds the path from the source point to The shortest path to each other vertex.

The basic idea of ​​Dijkstra algorithm

        First assume that the source point is u, and the vertex set V is divided into two parts: sets S and VS. Initially, S only contains the source point u, and the shortest path from the vertices in S to the source point has been determined.
        The length of the shortest path from the vertices contained in the sets S and VS to the source point is to be determined, and the path starting from the source point and only passing through the points in S to the points in VS is called a special path, and record each current vertex with dist[] The corresponding shortest special path length.

Dijkstra Greedy Strategy

        Select the path with the shortest length of the special path, add the vertices in the connected VS to the set S, and update the array dist[] at the same time. Once S contains all vertices, dist[] is the shortest path length from source to all other vertices.

  • (1) Data structure. Set the weighted adjacency matrix of the map to map[][], that is, if there is an edge from the source point u to the vertex i, let map[u][i]=<u,i>The weight value, otherwise map[u] [i]=∞; use one-dimensional array dist[i] to record the length of the shortest path from the source point to i vertex: use one-dimensional array p[i] to record the predecessor of i vertex on the shortest path.
  • (2) Initialization. Let the set S={u}, for all vertices x in the set VS, initialize dist[i]=map[u][i], if there is an edge connecting the source point u to vertex i, initialize p[i]=u( The predecessor of i is u), otherwise p[i]=-1
  • (3) Find the minimum. In the set VS, find the vertex t that makes dist[j] have the minimum value according to the greedy strategy, that is, dist[t]=min, then the vertex t is the vertex closest to the source point u in the set VS.
  • (4) Join the S team. Add vertex t to set S and update VS
  • (5) The sentence is over. If the set VS is empty, the algorithm ends, otherwise go to 6
  • (6) Borrow the east wind. In (3), the shortest path from the source point to t has been found, so for all vertices j adjacent to vertex t in the set VS, you can use t to take a shortcut. If dist[j]>dist[t]+map[t][j], then dist[j]=dist[t]+map[t][j], record the predecessor of vertex j as t, p[j] =t, turn to (3).

        My own understanding here is to find the nearest point t from u, find the nearest point j from t, and continue in this way until the last point. Here I will explain this in a popular way Borrow the meaning of the east wind. The source point is 1, if we find the point 2 closest to the source point, and point 2 is connected to 3,4. In this way, if we want to pour 3 and 4, there are two ways:

  •                 1->2->3(4)
  •                 1->3(4)

        Here we have to judge whether it is fast from 1 to 3 (4) directly, or after 2. Assuming <1,2>=2 / <2,3>=3 / <1,3>=4, according to the above data, the first time we find the minimum is 2 nodes, if we directly replace 2 1 as the source point to continue to find the next nearest point, this method is wrong.

        Because it can be seen that only 4 is used for 1->3, and 5 is used for passing 2.

perfect illustration

Here I just put the pictures directly, the pictures in the book are not easy to draw. But the main thing is to go through the process by yourself and draw it on the draft paper by yourself.

insert image description hereinsert image description hereinsert image description hereinsert image description here     

Detailed Pseudocode

After following the illustrations for a general understanding, the next step is to upload the code. Don’t worry, the code is not a complete code of dozens of lines. It is all divided into steps. It is convenient for everyone to paste here.

/*
(1)数据结构
	n:城市顶点个数. m:城市间路线的条数. map[][]:地图对应的带权邻接矩阵. dist[]:记录源点u到某顶点的最短路径长度。
	p[]:记录源点到某顶点的最短路径上的该顶点的前一个顶点(前驱).flag[]:flag[i]=true说明顶点i已加入到集合S,否则该顶点属于集合V-S
*/
	const int N=100;//初始化城市个数,可修改
	const int INF=1e7;	//无穷大
	int map[N][N],dist[N],p[N],n,m;
	bool flag[N];

//(2)初始化源点u到其他各个顶点的最短路径长度,初始化源点u出边邻接点的前驱为u
	bool flag[n];//如果flag[i]=true,说明该顶点i已经加入到集合S;否则i属于集合V-S
	for(int i=1;i<=n;i++){
		dist[i]=map[u][i];	//初始化源点u到其他各个顶点的最短路径长度
		flag[i]=false;
		if(dist[i]==INF)
			p[i]=-1;	//说明源点u到顶点i无边相连,设置p[i]=-1
		else
			p[i]=u;	//说明源点u到顶点i有边相连,设置p[i]=u
	}
//(3)初始化集合S,令集合S={u},从源点u的最短路径为0
	flag[u]=true;//初始化集合S中,只有一个元素:源点u
	dist[u]=0;	//初始化源点u的最短路径为0,自己到自己的最短路径

//(4)找最小.在集合V-S中寻找距离源点u最近的顶点t,若找不到,则跳出循环;否则,将t加入集合S。
	int temp=INF,t=u;
	for(int j=1;j<=n;j++){//在集合V-S中寻找距离源点u最近的顶点t
		if(!flag[j] && dist[j]<temp){
			t=j;	//记录距离源点u最近的顶点
			temp=dist[j];
		}
	}
	if(t==u) return ;	//找不到t跳出循环
	flag[t]=true;	//否则,将t加入集合S
	
//(5)借东风。考察集合V-S中源点u到t的邻接点j的距离,如果源点u经过t到达j的路径更短,
//	则更新dist[j]=dist[t]+map[t][j],即松弛操作,并记录j的前驱为t;
	for(int j=1;j<=n;j++){//更新集合V-S中与t邻接的顶点到u的距离
		if(!flag[j] && map[t][j]<INF){//!flag[j]表示j在v-s集合中,map[t][j]<INF表示t与j邻接
			if(dist[j]>(dist[t]+map[t][j])){//经过t到达j的路径更短
				dist[j]=dist[t]+map[t][j];
				p[j]=t;	//记录j的前驱为t
			}
		}
	}	
//重复(4)~(5),知道源点u到所有顶点的最短路径被找到

full code

#include<bits/stdc++.h>
using namespace std;
const int N=100;	//城市个数可修改
const int INF=1e7;	//初始化无穷大为.......
int map[N][N],dist[N],p[N],n,m;	//n为城市个数,m为城市间路线的条数
bool flag[N];	//如果flag[i]=true,说明该顶点i已经加入到集合S;否则i属于集合V-S

void Dijkstra(int u){
	for(int i=1;i<=n;i++){//********>>>--1--<<<******//
		dist[i]=map[u][i];	//初始化源点u到其他各个顶点的最短路径长度
		flag[i]=false;
		if(dist[i]==INF)
			p[i]=-1;	//说明源点u到顶点i无边相连,设置p[i]=-1
		else
			p[i]=u;	//说明源点u到顶点i有边相连,设置p[i]=u
	}
	flag[u]=true;//初始化集合S中,只有一个元素:源点u
	dist[u]=0;	//初始化源点u的最短路径为0,自己到自己的最短路径
	for(int i=1;i<=n;i++){//********>>>--2--<<<******//
		int temp=INF,t=u;
		for(int j=1;j<=n;j++){//>>--3--<<在集合V-S中寻找距离源点u最近的顶点t
			if(!flag[j] && dist[j]<temp){
				t=j;	//记录距离源点u最近的顶点
				temp=dist[j];
			}
		}
		if(t==u) return ;	//找不到t跳出循环
		flag[t]=true;	//否则,将t加入集合S
		for(int j=1;j<=n;j++){//>>--4--<<更新集合V-S中与t邻接的顶点到u的距离
			if(!flag[j] && map[t][j]<INF){//!flag[j]表示j在v-s集合中,map[t][j]<INF表示t与j邻接
				if(dist[j]>(dist[t]+map[t][j])){//经过t到达j的路径更短
					dist[j]=dist[t]+map[t][j];
					p[j]=t;	//记录j的前驱为t
				}
			}
		}	
	}	
}

int main(){
		int u, v, w, st;
	system("color 0d");
	cout << "请输入城市的个数:" << endl;
	cin >> n;
	cout << "请输入城市之间的路线个数" << endl;
	cin >> m;
	cout << "请输入城市之间的路线以及距离" << endl;
	for(int i=1;i<=n;i++)//初始化图的邻接矩阵
		for (int j = 1; j <= n; j++)
		{
			map[i][j] = INF;//初始化邻接矩阵为无穷大
		}
	while (m--)
	{
		cin >> u >> v >> w;
		map[u][v] = min(map[u][v], w);	//邻接矩阵存储,保留最小的距离
	}
	cout << "请输入小明所在的位置:" << endl;
	cin >> st;
	Dijkstra(st);
	cout << "小明所在的位置:" << st << endl;
	for (int i = 1; i <= n; i++)
	{
		cout << "小明:" << st << " - " << "要去的位置:" << i << endl;
		if (dist[i] == INF)
			cout << "sorry,无路可达" << endl;
		else
			cout << "最短距离为:" << dist[i] << endl; 
	}
	return 0;
}
输入
请输入城市的个数:
5
请输入城市之间的路线个数
11
请输入城市之间的路线以及距离
1 5 2
5 1 8
1 2 16
2 1 29
5 2 32
2 4 13
4 2 27
1 3 15
3 1 21
3 4 7
4 3 19
请输入小明所在的位置:
5

输出
小明所在的位置:5
小明:5 - 要去的位置:1 最短距离为:8
小明:5 - 要去的位置:2 最短距离为:24
小明:5 - 要去的位置:3 最短距离为:23
小明:5 - 要去的位置:4 最短距离为:30
小明:5 - 要去的位置:5 最短距离为:0
因为我们在程序中使用了p[]数组记录了最短路径上每一个结点的前驱,所以我们可以增加一段程序逆向该最短路径上的城市序列。
void findpath(int u)
{
	int x;
	stack<int>s;
	cout << "源点为:" << u << endl;
	for (int i = 1; i <= n; i++)
	{
		x = p[i];
		while (x != -1)
		{
			s.push(x);
			x = p[x];
		}
		cout << "源点到其他各顶点的最短路径为:";
		while (!s.empty())
		{
			cout << s.top() << "--";
			s.pop();
		}
		cout << i << ";最短距离为:" << dist[i] << endl;
	}
}
只需要在主函数末尾调用即可

结果为:
源点为:5
源点到其他各顶点的最短路径为:5--1;最短距离为:8
源点到其他各顶点的最短路径为:5--1--2;最短距离为:24
源点到其他各顶点的最短路径为:5--1--3;最短距离为:23
源点到其他各顶点的最短路径为:5--1--3--4;最短距离为:30
源点到其他各顶点的最短路径为:5;最短距离为:0

Algorithm analysis and optimization expansion

insert image description here
insert image description here

Complete code using priority queue

#include<queue>
#include<iostream>
#include<algorithm>
using namespace std;
const int N = 100;//城市的个数可修改
const int INF = 1e7;//初始化无穷大为10000000
int map[N][N], dist[N], p[N], n, m;//n为城市的个数,m为城市间路线的条数
int flag[N];	//	如果flag[i]==true,说明顶点i已经加入到集合S;否则顶点i属于集合V-S

struct Node {
	int u, step;
	Node() {};
	Node(int a, int sp)
	{
		u = a, step = sp;
	}
	bool operator<(const Node& a)const {//重载 <
		return step > a.step;
	}
};

void Dijkstra(int st)
{
	priority_queue<Node>Q;//优先队列优化
	Q.push(Node(st, 0));
	memset(flag, 0, sizeof(flag));//初始化flag数组为0
	for (int i = 1; i <= n; ++i)
		dist[i] = INF;//初始化所有距离为无穷大
	dist[st] = 0;
	while (!Q.empty())
	{
		Node it = Q.top();//优先队列列头元素为最小值
		Q.pop();
		int t = it.u;
		if (flag[t])//说明已经找到了最短距离,该节点是队列里面的重复元素
			continue;
		flag[t] = 1;
		for (int i = 1; i <= n; i++)
		{
			if(!flag[i] && map[t][i]<INF)//判断与当前点有关系的点,并且自己不能到自己
				if (dist[i] > dist[t] + map[t][i])
				{
					//求距离当前点的每个点的最短距离,进行松弛操作
					dist[i] = dist[t] + map[t][i];
					Q.push(Node(i, dist[i]));//把更新后的最短距离压入队列中,注意:里面有重复元素
				}
		}
	}

}

int main()
{
	int u, v, w, st;
	system("color 0d");
	cout << "请输入城市的个数:" << endl;
	cin >> n;
	cout << "请输入城市之间的路线个数" << endl;
	cin >> m;
	cout << "请输入城市之间的路线以及距离" << endl;
	for (int i = 1; i <= n; i++)//初始化图的邻接矩阵
		for (int j = 1; j <= n; j++)
		{
			map[i][j] = INF;//初始化邻接矩阵为无穷大
		}
	while (m--)
	{
		cin >> u >> v >> w;
		map[u][v] = min(map[u][v], w);	//邻接矩阵存储,保留最小的距离
	}
	cout << "请输入小明所在的位置:" << endl;
	cin >> st;
	Dijkstra(st);
	cout << "小明所在的位置:" << st << endl;
	for (int i = 1; i <= n; i++)
	{
		cout << "小明:" << st << " ---> " << "要去的位置:" << i;
		if (dist[i] == INF)
			cout << "sorry,无路可达" << endl;
		else
			cout << " 最短距离为:" << dist[i] << endl;
	}
	return 0;
}

/*
请输入城市的个数:
5
请输入城市之间的路线个数
11
请输入城市之间的路线以及距离
1 5 2
5 1 8
1 2 16
2 1 29
5 2 32
2 4 13
4 2 27
1 3 15
3 1 21
3 4 7
4 3 19
请输入小明所在的位置:
5
小明所在的位置:5
小明:5 ---> 要去的位置:1 最短距离为:8
小明:5 ---> 要去的位置:2 最短距离为:24
小明:5 ---> 要去的位置:3 最短距离为:23
小明:5 ---> 要去的位置:4 最短距离为:30
小明:5 ---> 要去的位置:5 最短距离为:0
*/

insert image description here

Guess you like

Origin blog.csdn.net/m0_64560763/article/details/131122548