1. Definição:
emGráfico não direcionado conectadoG(V,E)
Encontre um E
subconjunto acíclico de arestas em T
, de modo que possa conectar todos os nós e ter o menor peso.
a)Como T é acíclico, pode ser considerado uma árvore;
B)Uma vez que é gerado pelo grafo G, é chamado de (grafo G) árvore geradora;
C)Uma vez que T tem o menor peso, é chamada de árvore geradora mínima.
Como T é acíclico, os vértices V devem ter arestas V-1.
2. Estratégia gananciosa:
cada vez que uma aresta da árvore de abrangência mínima cresce, os ciclos V-1 são executados para completar a construção da árvore. As bordas de crescimento devem ser bordas leves (peso mínimo) e não formar um anel.
Método: algoritmo kruskal (Kruskal) ou algoritmo prim (primo)
3. Algoritmo Prim (primo):
Este algoritmo pode ser chamado de método de adição de pontos.Cada iteração seleciona o ponto correspondente à aresta de menor custo e adiciona-o à árvore geradora mínima. O algoritmo começa a partir de qualquer vértice se cresce gradualmente para cobrir todos os vértices de todo o gráfico conectado.
3.1. Fluxo e gráfico do algoritmo:
Entrada: gráfico G(V,E)
e qualquer nó u
como ponto de partida; saída: conjunto de arestas P
- O nó
u
se junta à coleção de nósU
; - Selecione a
U
aresta de menor custo conectada aos nós do conjunto para unir o conjuntoP
(como arestas<u,v>
) ev
unir os nós conectados (como ) ao conjuntoU
;
(se houver várias arestas com o mesmo peso, você pode escolher um deles à vontade); - Repita a etapa 2 até que todos os nós se juntem ao conjunto
U
.
3.2. Código 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. Algoritmo de Kruskal (Kruskal):
Este algoritmo pode ser chamado de método de borda aditiva . O número mínimo inicial da borda da árvore geradora é 0. Cada iteração seleciona uma borda de custo mínimo que satisfaz a condição e a adiciona ao conjunto de arestas da árvore geradora mínima no.
4.1. Fluxo e diagrama do algoritmo:
entrada: gráfico G(V,E)
; saída: conjunto de arestas P.
- Classifique todas as arestas no gráfico do menor ao maior custo;
- Considere os n vértices no gráfico como uma floresta de n árvores independentes;
- Selecione as arestas do menor ao maior peso,
u,v
Se os dois vértices da aresta selecionada pertencem a duas árvores diferentes, eles se tornam uma aresta da árvore de abrangência mínima e as duas árvores são fundidas em uma árvore. Caso contrário, pegue a borda com o menor peso e tente novamente. - Repita (3) até que todos os vértices estejam em uma árvore ou haja n-1 arestas.
4.2. Código 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];
}
}
}
}
}
Material de referência: Dois métodos de spanning tree mínimo (algoritmo de Kruskal e algoritmo de Prim)
Introdução ao algoritmo 23.2 Algoritmo de Kruskal e algoritmo de Prim
Resumindo:
1. O algoritmo kruskal é o método de adição, o algoritmo prim é o método de adição e a ideia do algoritmo de memória é boa.