[Lenguaje C] Aprendizaje de algoritmos · Explicación del algoritmo de Dijkstra

Tabla de contenido

Diseño del algoritmo de Dijkstra

Introducción al Algoritmo de Dijkstra

La idea básica del algoritmo de Dijkstra

Estrategia codiciosa de Dijkstra

ilustración perfecta

Pseudocódigo detallado

código completo

Expansión de optimización y análisis de algoritmos

​Código completo usando la cola de prioridad


Diseño del algoritmo de Dijkstra

Introducción al Algoritmo de Dijkstra

        El algoritmo de Dijkstra es un **algoritmo codicioso** que resuelve el problema de la **ruta más corta de fuente única**.
        Primero encuentra una ruta con la longitud más corta y luego se refiere a la ruta más corta para encontrar una ruta con la segunda longitud más corta. hasta que encuentre el camino desde el punto de origen hasta el vértice más corto entre sí.

La idea básica del algoritmo de Dijkstra

        Primero suponga que el punto de origen es u, y el conjunto de vértices V se divide en dos partes: los conjuntos S y VS. Inicialmente, S solo contiene el punto de origen u, y se ha determinado el camino más corto desde los vértices de S hasta el punto de origen.
        Se va a determinar la longitud del camino más corto desde los vértices contenidos en los conjuntos S y VS hasta el punto fuente, y el camino que comienza desde el punto fuente y solo pasa por los puntos en S hasta los puntos en VS se llama camino especial. ruta y registre cada vértice actual con dist[] La longitud de ruta especial más corta correspondiente.

Estrategia codiciosa de Dijkstra

        Seleccione la ruta con la longitud más corta de la ruta especial, agregue los vértices en el VS conectado al conjunto S y actualice la matriz dist[] al mismo tiempo. Una vez que S contiene todos los vértices, dist[] es la longitud de ruta más corta desde el origen hasta todos los demás vértices.

  • (1) Estructura de datos. Establezca la matriz de adyacencia ponderada del mapa en map[][], es decir, si hay un borde desde el punto de origen u hasta el vértice i, sea map[u][i]=<u,i> peso, de lo contrario map[u] [i]=∞; use el arreglo unidimensional dist[i] para registrar la longitud del camino más corto desde el punto de origen hasta el vértice i: use el arreglo unidimensional p[i] para registrar el predecesor de i vértice en el camino más corto.
  • (2) Inicialización. Sea el conjunto S={u}, para todos los vértices x en el conjunto VS, inicialice dist[i]=map[u][i], si hay un borde que conecta el punto de origen u con el vértice i, inicialice p[i ]=u( El predecesor de i es u), de lo contrario p[i]=-1
  • (3) Encuentra el mínimo. En el conjunto VS, encuentra el vértice t que hace que dist[j] tenga el valor mínimo según la estrategia voraz, es decir, dist[t]=min, entonces el vértice t es el vértice más cercano al punto fuente u en el establecer VS.
  • (4) Únete al equipo S. Agregue el vértice t para establecer S y actualice VS
  • (5) La sentencia ha terminado. Si el conjunto VS está vacío, el algoritmo finaliza, de lo contrario, vaya a 6
  • (6) Toma prestado el viento del este. En (3), se encontró la ruta más corta desde el punto de origen hasta t, por lo que para todos los vértices j adyacentes al vértice t en el conjunto VS, puede usar t para tomar un atajo. Si dist[j]>dist[t]+map[t][j], entonces dist[j]=dist[t]+map[t][j], registre el predecesor del vértice j como t, p[j ] =t, pase a (3).

        Mi propio entendimiento aquí es encontrar el punto t más cercano a u, encontrar el punto j más cercano a t, y continuar de esta manera hasta el último punto. Aquí explicaré esto de una manera popular Toma prestado el significado del viento del este. El punto fuente es 1, si encontramos el punto 2 más cercano al punto fuente, y el punto 2 está conectado a 3,4. De esta forma, si queremos verter 3 y 4, hay dos formas:

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

        Aquí tenemos que juzgar si es rápido del 1 al 3 (4) directamente, o después del 2. Suponiendo <1,2>=2 / <2,3>=3 / <1,3>=4, según los datos anteriores, la primera vez que encontramos el mínimo son 2 nodos, si reemplazamos directamente 2 1 como el punto de origen para continuar buscando el siguiente punto más cercano, este método es incorrecto.

        Porque se puede ver que solo 4 se usa para 1->3, y 5 se usa para pasar 2.

ilustración perfecta

Aquí solo pongo las imágenes directamente, las imágenes en el libro no son fáciles de dibujar. Pero lo principal es pasar por el proceso usted mismo y dibujarlo en el borrador por sí mismo.

inserte la descripción de la imagen aquíinserte la descripción de la imagen aquíinserte la descripción de la imagen aquíinserte la descripción de la imagen aquí     

Pseudocódigo detallado

Después de seguir las ilustraciones para una comprensión general, el siguiente paso es cargar el código. No te preocupes, el código no es un código completo de decenas de líneas. Está todo dividido en pasos. Es conveniente que todos peguen aquí. .

/*
(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到所有顶点的最短路径被找到

código completo

#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

Expansión de optimización y análisis de algoritmos

inserte la descripción de la imagen aquí
inserte la descripción de la imagen aquí

Código completo usando la cola de prioridad

#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
*/

inserte la descripción de la imagen aquí

Supongo que te gusta

Origin blog.csdn.net/m0_64560763/article/details/131122548
Recomendado
Clasificación