概念 最小生成樹 MST |
一顆無迴路的 有 v-1個邊 的樹 |
包含全部的結點的 生成樹 |
生成樹:任意加一條邊都一定會構成迴路 |
Prim 算法
讓一顆樹長大 |
1. 把一個初始點加入到 待拿取的數組中 (我是用的優先隊列 - 下面再說) |
2. 開始循環 現在第一個初始點就是一個樹 我們要找離這個 樹權值最小的節點 |
3. 用數組的話 可以跑循環來找,而優先隊列 = 堆,我把他設置成一個最小堆,然後每次拿取第一個元素 |
4. 拿了之後就是把他加入到樹中成爲樹的一部分,現在就要更新別的的點了,我們有一個專門的數組dist(即distance 間距) – 他用來放每個點距離樹的權值,現在這個點已經是樹的一部分了,所以這個點的鄰接點距離樹的權值就需要改變 |
過程就是這麼個過程 |
下面看代碼 註釋很詳細 我是用的鄰接表 |
不懂優先隊列的可以去點下面的鏈接 用法很簡單 |
優先隊列
適用 特點 |
時間複雜度 |
鄰 接 表 : O(ElogN) |
鄰接矩陣 :O(N*N) |
與圖中的邊數無關 |
考點:因此適用於求邊稠密的網 |
稠密圖 不管鄰接矩陣還是鄰接表 都是N*N |
Prim算法是依賴於點的算法 |
#include <iostream>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
using pii = pair<int, int>;
#define MAX 100
const int inf = 0x3f3f3f3f;
struct ENode
{
int v1, v2;
int weight;
};
vector<ENode> MST;
int dist[MAX];
int parent[MAX];
bool used[MAX];
int N;
struct node
{
int x;
int weight;
};
vector<node> rode[MAX];
int quan[MAX];
int Prim()
{
int totalWeight = 0, VCount = 0;
fill(dist, dist + N, inf);
fill(parent, parent + N, -1);
dist[0] = 0;
priority_queue<pii, vector<pii>, greater<pii>> q;
q.push({0, 0});
while (!q.empty())
{
pii x = q.top();
q.pop();
int d = x.first, v = x.second;
if (used[v])
continue;
MST.push_back({parent[v], v, d});
totalWeight += dist[v];
used[v] = true;
for (auto e : rode[v])
{
if (e.weight < dist[e.x])
{
dist[e.x] = e.weight;
parent[e.x] = v;
q.push({dist[e.x], e.x});
}
}
}
cout << "v1->v2 weight" << endl;
for (auto e : MST)
{
if (e.v1 == -1)
continue;
cout << e.v1 << "->" << e.v2 << " " << e.weight << endl;
}
return MST.size() == N ? totalWeight : -1;
}
int main()
{
int M;
cin >> N >> M;
for (int i = 0; i < M; i++)
{
int x, y, d;
cin >> x >> y >> d;
rode[x].push_back({y, d});
rode[y].push_back({x, d});
}
int ans = Prim();
cout << ans << endl;
return 0;
}
樣例
輸入
9 15
0 1 2
0 5 7
0 6 3
1 2 4
1 6 6
2 3 2
2 7 2
3 7 8
3 4 1
4 8 2
4 5 6
5 8 5
8 6 1
8 7 4
7 6 3
輸出
v1->v2 weight
0->1 2
0->6 3
6->8 1
8->4 2
4->3 1
3->2 2
2->7 2
8->5 5
18
Kruskal算法 克魯斯卡爾
將森林合併成樹 |
以邊爲單位進行存儲 兩點+權重 |
1. 以權重進行排序(有的是寫一個最小堆 和直接排序時間一樣 排序好些啊) |
2. 然後依次掃描邊集合 |
現在所有的邊都是按照權重進行排序的 |
3. 掃描的時候看看這兩個點是不是已經連通了 就用到了並查集 |
4. 沒連通連上即可 連通了就不用管了 一定是之前有更小的權重和別的的點連上了 |
我感覺Kruskal寫起來簡單 理解也不難 |
適用 分析 |
時間複雜度 |
O(ElongE) E是邊的數目 |
適用於邊稀疏的網 |
Kruskal算法是依賴邊的算法 |
在邊越少的情況下,Kruskal算法相對Prim算法的優勢就越大 |
#include <iostream>
#include <algorithm>
#include <vector>
#include <numeric>
using namespace std;
int N, M;
struct edge
{
int x, y, weight;
};
vector<edge> edges;
vector<edge> MST;
vector<int> p;
int find(int x)
{
return p[x] == x ? x : p[x] = find(p[x]);
}
int Kruskal()
{
int SumWeight = 0, cnt = 0;
sort(edges.begin(), edges.end(), [](auto &e1, auto &e2) { return e1.weight < e2.weight; });
p.resize(N + 1);
iota(p.begin(), p.end(), 0);
for (auto &e : edges)
{
int x = e.x;
int y = e.y;
int d = e.weight;
x = find(x);
y = find(y);
if (x == y)
continue;
SumWeight += d;
p[x] = y;
MST.push_back({e.x, e.y, e.weight});
if (++cnt == N - 1)
break;
}
return cnt == N - 1 ? SumWeight : -1;
}
int main()
{
cin >> N >> M;
edges.resize(N);
for (int i = 0; i < M; i++)
{
int x, y, d;
cin >> x >> y >> d;
edges.push_back({x, y, d});
}
cout << Kruskal();
cout << endl;
for (auto e : MST)
{
cout << e.x << "->" << e.y << " " << e.weight << " \n";
}
return 0;
}