【C言語】アルゴリズム学習・ダイクストラアルゴリズム解説

目次

ダイクストラアルゴリズムの設計

ダイクストラのアルゴリズムの概要

ダイクストラアルゴリズムの基本的な考え方

ディクストラの強欲戦略

完璧なイラスト

詳細な擬似コード

完全なコード

アルゴリズム解析と最適化の拡張

優先キューを使用してコードを完成させる


ダイクストラアルゴリズムの設計

ダイクストラのアルゴリズムの概要

        ダイクストラのアルゴリズムは、**単一ソース最短パス** 問題を解決する **貪欲なアルゴリズム**です。
        最初に最短の長さのパスを見つけ、次に最短パスを参照して 2 番目に短い長さのパスを見つけます。ソースポイントから他の頂点への最短パスまでのパスが見つかるまで。

ダイクストラアルゴリズムの基本的な考え方

        まず、ソース点が u であり、頂点セット V がセット S と VS の 2 つの部分に分割されていると仮定します。最初に、S にはソース点 u のみが含まれており、S の頂点からソース点までの最短パスが決定されています。
        集合 S および VS に含まれる頂点からソース点までの最短経路の長さを決定します。ソース点から開始し、S 内の点のみを通過して VS 内の点までのパスを特殊パスと呼びます。 path を作成し、現在の各頂点を dist[] で記録します。対応する最短の特別なパスの長さ。

ディクストラの強欲戦略

        特殊なパスのうち長さが最も短いパスを選択し、接続されている VS 内の頂点を集合 S に追加し、同時に配列 dist[] を更新します。S にすべての頂点が含まれると、dist[] はソースから他のすべての頂点までの最短パス長になります。

  • (1) データ構造。マップの重み付けされた隣接行列を map[][] に設定します。つまり、ソース点 u から頂点 i までのエッジがある場合は、map[u][i]=<u,i> Weight とし、それ以外の場合は、重み付けします。 map[u] [i]=∞; 1 次元配列 dist[i] を使用してソース点から i 頂点までの最短パスの長さを記録します: 1 次元配列 p[i] を使用して i の前任者を記録します最短経路上の頂点。
  • (2) 初期化。セット S={u} とし、セット VS 内のすべての頂点 x について、dist[i]=map[u][i] を初期化します。ソース点 u を頂点 i に接続するエッジがある場合、p[i を初期化します。 ]=u(i の前任者は u)、それ以外の場合は p[i]=-1
  • (3) 最小値を求めます。集合 VS 内で、貪欲戦略に従って dist[j] が最小値を持つ頂点 t を見つけます。つまり、dist[t]=min になります。その場合、頂点 t は、集合内のソース点 u に最も近い頂点になります。 VSを設定します。
  • (4) Sチームに参加します。頂点 t を追加して S を設定し、VS を更新します
  • (5) 刑期は終わりました。セット VS が空の場合、アルゴリズムは終了し、そうでない場合は 6 に進みます。
  • ⑥東風を借りる。(3) では、ソース点から t までの最短経路が見つかったので、集合 VS 内の頂点 t に隣接するすべての頂点 j について、t を使用してショートカットを実行できます。dist[j]>dist[t]+map[t][j] の場合、dist[j]=dist[t]+map[t][j]、頂点 j の前任者を t, p[j として記録します。 ] =t、(3)に進みます。

        ここでの私自身の理解は、u から最も近い点 t を見つけ、t から最も近い点 j を見つけ、この方法を最後の点まで続けることです。ここでは、これを一般的な方法で説明します。東風の意味を借ります。ソース ポイントに最も近いポイント 2 が見つかった場合、ソース ポイントは 1 となり、ポイント 2 は 3、4 に接続されます。このように、3と4を注ぎたい場合は、次の2つの方法があります。

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

        ここで、1から3(4)までが直接速いのか、2以降が速いのかを判断する必要があります。<1,2>=2 / <2,3>=3 / <1,3>=4 と仮定すると、上記のデータによれば、2 1 を直接置き換えた場合、最小値は 2 ノードであることが初めてわかります。ソース ポイントを使用して次に近いポイントを検索し続ける場合、この方法は間違っています。

        1→3には4しか使われておらず、2を渡すには5が使われていることがわかるからです。

完璧なイラスト

ここでは絵を直接載せているだけですが、本の中の絵は描くのが簡単ではありません。しかし、重要なことは、自分でプロセスを経て、自分で原稿用紙に描くことです。

ここに画像の説明を挿入ここに画像の説明を挿入ここに画像の説明を挿入ここに画像の説明を挿入     

詳細な擬似コード

イラストに従って全体を理解したら、次のステップはコードをアップロードすることです。コードは数十行の完全なコードではありませんので、ご安心ください。すべてステップに分かれています。ここに貼り付けると誰でも便利です。 。

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

完全なコード

#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

アルゴリズム解析と最適化の拡張

ここに画像の説明を挿入
ここに画像の説明を挿入

優先キューを使用してコードを完成させる

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

ここに画像の説明を挿入

おすすめ

転載: blog.csdn.net/m0_64560763/article/details/131122548