最小全域木を構築する貪欲アルゴリズム:クラスカルアルゴリズム、プリムアルゴリズム

1.定義:
接続された無向グラフG(V,E)Eエッジの非循環サブセットを見つけて、Tすべてのノードを接続し、重みが最小になるようにします。
a)Tは非循環なので、ツリーと見なすことができます。
b)これはグラフGによって生成されるため、(グラフG)スパニングツリーと呼ばれます。
c)Tは最小の重みを持つため、最小全域木と呼ばれます。
Tは非循環なので、V頂点にはV-1エッジが必要です。

2.貪欲戦略:
最小全域木のエッジが成長するたびに、V-1サイクルが実行されてツリーの構築が完了します。成長するエッジは、軽量のエッジ(最小重量)である必要があり、リングを形成しないでください。
方法: kruskal(Kruskal)アルゴリズムまたはprim(prime)アルゴリズム

3. Prim(prime)アルゴリズム:
このアルゴリズムは、ポイント追加メソッドと呼ばれます。各反復は、最小コストのエッジに対応するポイントを選択し、最小スパニングツリーに追加します。アルゴリズムは、任意の頂点から始まり、接続されたグラフ全体のすべての頂点をカバーするように徐々に拡大します。

3.1。アルゴリズムのフローとグラフ:
入力:グラフG(V,E)u開始点としてのノード、出力:エッジセットP

  1. ノードuはノードコレクションに参加しますU
  2. Uセット内のノードに接続されている最小コストのエッジP(エッジなど<u,v>)を選択し、接続されているノード(などv)をセットに結合しUます
    (同じ重みのエッジが複数ある場合は、それらの1つを自由に選択できます);
  3. すべてのノードがセットに参加するまで、手順2を繰り返しUます。

ここに画像の説明を挿入
3.2。C ++コード:

#include <queue>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct edge {
    
     // 边的定义
	int start;
	int end;
	int weight;
	edge(int x, int y, int z) :start(x), end(y), weight(z) {
    
    }
	bool operator< (const edge& tmp) const {
    
     //用于优先队列的比较函数
		return weight > tmp.weight;
	}
};

const int NUM = 6;
int G[NUM][NUM]; //假设图已经定义好了,如G[0][2] = 5; 5为权重
void MiniSpanTree_prim(int(*G)[NUM], int root)
{
    
    
	vector<int> V; //记录MST已经包含的点;
	V.push_back(root);
	priority_queue<edge> edge_all; // 放入权重边,自动排序
	for (int i = 0; i < NUM; i++) {
    
     // 放入与起点连接的边
		if (G[root][i] != 0) {
    
     // 0 代表没有路
			edge tmp(root, i, G[root][i]);
			edge_all.push(tmp);
		}
	}
	cout << "Prim :" << endl;
	for (int i = 0; i < NUM - 1; i++) {
    
     //共N-1条边
		edge curr = edge_all.top(); //取得代价最小边
		edge_all.pop();
		while (find(V.begin(), V.end(), curr.end) != V.end()) {
    
     //边终点若已包含,则丢弃后换一条边
			curr = edge_all.top();
			edge_all.pop();
		}
		V.push_back(curr.end); //放入这条边的终点v
		cout << curr.start << " --> " << curr.end << " " << curr.weight << endl; // 输出MST的边
		for (int j = 0; j < NUM; j++) {
    
     // 加入终点v连接的边
			if (G[curr.end][j] != 0 && find(V.begin(), V.end(), j) == V.end()) {
    
    
				edge tmp(curr.end, j, G[curr.end][j]);
				edge_all.push(tmp);
			}
		}
	}
}

4. Kruskal(Kruskal)アルゴリズム:
このアルゴリズムは加法エッジ法と呼ばれ、最初の最小スパニングツリーエッジ番号は0で、反復ごとに条件を満たす最小コストエッジが選択され、最小スパニングツリーのエッジセットに追加されます。に。

4.1。アルゴリズムのフローと図:
入力:グラフG(V,E);出力:エッジセットP.

  1. グラフのすべてのエッジを最小コストから最大コストまで並べ替えます。
  2. グラフのn個の頂点を、独立したn本の木の森と見なします。
  3. 最小から最大の重みまでエッジを選択、選択したエッジの2つの頂点がu,v2つの異なるツリーに属している場合、それらは最小スパニングツリーのエッジになり、2つのツリーは同時に1つのツリーにマージされます。それ以外の場合は、重みが最小のエッジを取り、再試行してください。
  4. すべての頂点がツリー内にあるか、n-1エッジになるまで(3)を繰り返します。

ここに画像の説明を挿入

4.2。C ++コード:

#include <queue>
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
struct edge {
    
     // 边的定义
	int start;
	int end;
	int weight;
	edge(int x, int y, int z) :start(x), end(y), weight(z) {
    
    }
	bool operator< (const edge& tmp) const {
    
     //用于优先队列的比较函数
		return weight > tmp.weight;
	}
};

const int NUM = 6;
int G[NUM][NUM]; //假设图已经定义好了,如G[0][2] = 5; 5为权重
void MiniSpanTree_kruskal(int(*G)[NUM])
{
    
    
	int node_coll[NUM] = {
    
     0 }; //判断节点是否属于一棵树
	int count = 1; // 用于不同树的标记
	priority_queue<edge> edge_all; // 放入权重边,自动排序
	for (int i = 0; i<NUM; i++) {
    
     //放入所有边,并自动排序
		for (int j = 0; j<NUM; j++) {
    
    
			if (G[i][j] != 0) {
    
    
				edge tmp(i, j, G[i][j]);
				edge_all.push(tmp);
			}
		}
	}
	for (int i = 0; i<NUM - 1; i++) {
    
     //共N-1条边
		edge tmp = edge_all.top(); // 取代价最小边
		edge_all.pop();
		while (node_coll[tmp.start] == node_coll[tmp.end] && node_coll[tmp.start] != 0 && node_coll[tmp.end] != 0) {
    
     // 找到属于不同树的边
			tmp = edge_all.top();
			edge_all.pop();
		}
		cout << tmp.start << " --> " << tmp.end << " " << tmp.weight << endl; // 输出MST的边
		if (node_coll[tmp.start] == 0 && node_coll[tmp.end] == 0) {
    
     //不同树的边的情况1
			node_coll[tmp.start] = count;
			node_coll[tmp.end] = count;
			count++;
		}
		else if (node_coll[tmp.start] == 0 && node_coll[tmp.end] != 0) {
    
     //不同树的边的情况2
			node_coll[tmp.start] = count;
			node_coll[tmp.start] = node_coll[tmp.end];
		}
		else if (node_coll[tmp.start] != 0 && node_coll[tmp.end] == 0) {
    
     //不同树的边的情况3
			node_coll[tmp.start] = count;
			node_coll[tmp.end] = node_coll[tmp.start];
		}
		else if (node_coll[tmp.start] != 0 && node_coll[tmp.end] != 0) {
    
     //不同树的边的情况4
			node_coll[tmp.start] = count;
			for (int i = 0; i<NUM; i++) {
    
    
				if (node_coll[i] = node_coll[tmp.end]) {
    
    
					node_coll[i] = node_coll[tmp.start];
				}
			}
		}
	}
}

参考資料:2つの最小全域木の方法(クラスカルアルゴリズムとプリムアルゴリズム)アルゴリズムの
概要23.2クラスカルアルゴリズムとプリムアルゴリズム

総括する:

1.クラスカルアルゴリズムは加算方式であり、プリムアルゴリズムは加算方式であり、メモリアルゴリズムのアイデアは優れています。

おすすめ

転載: blog.csdn.net/qq_33726635/article/details/106024969