最小生成樹

其實我在學最短路之前就學了生成樹了,現在接着寫。
本文介紹2種算法:Kruskal,Prim
PS:文中分大小寫。 圖爲G(V,E),V爲節點集合,E爲邊集合,但v表示某個節點(v∈V)
其實很多都和最短路差不多的,鬆弛操作不同而已。
前提:連通圖

Kruskal:

  • 原理: 通過排序每一條邊(權值遞增)從|E|條邊中取V-1條邊出來(V個點嘛,最小生成樹始終是V-1條邊的),滿足每次選的邊的起點和終點不在一棵樹上。
  • 採用: 並查集
  • 步驟:
    1. 初始化邊,並以升序排序
    2. 初始化並查集,這裏以p域代表, p[i]=i; // i∈[1,|V|]
    3. 循環(1~|E| : i)
      1)判斷第i條邊的起點和終點是否在一棵樹上(使用並查集,可保證非常快的判斷)
      2)如果不在的話,加入i邊,並使得p[起點]=終點 或 p[終點]=起點

並查集實現:

inline int find(int x)
{
	int t = x;
	while(p[x] != x) x = p[x];
	p[t] = x;
	return x;
}
  • 分析:時間複雜度O(E) 並查集的時間太小 省略。
  • 適用於:任何連通圖 稀疏圖
  • 優化:我不會

Prim:

    • 原理:通過|V|-1次加入樹中與v的邊, min{key[v] | v∈G-已生成的最小樹} 到最小樹中,來實現最小生成樹
    • 採用:優先隊列 priority_queue (#include <queue> 要定義一個比較類來實現最小堆)
    • 步驟:
      1. 初始化key域 key[i] = INF; // i∈[1,|V|]
      2. key[S]=0; // S爲最小生成樹的根
      3. 初始化vis域(已生成樹),且vis[i]=0; // i∈[1,|V|]
      4. 將S壓入優先隊列q
      5. 循環(q不爲空)
        1)使u=q的隊頭,並將隊頭出隊
        2)把u與樹中的邊加入到樹中,即設置u已經在樹中vis[u]=1;
        3)對以u爲起點的邊進行僞鬆弛(這些邊的終點一定是不在樹中的,即vis[a]!=1)。並把這些邊的終點壓入隊列。

我說的僞鬆弛的指的是:if(key[i] > edge[u][i]) key[i] = edge[u][i];//注意,如果沒有u->i這條路徑,edge[u][i]一定要是INF,不要是0,這點在初始化時做
(如果初始化爲0的話,可以這樣 if(edge[u][i] && key[i] > edge[u][i]) key[i] = edge[u][i];)
(之所以叫僞鬆弛,因爲這和鬆弛太像了。)

  • 分析:時間複雜度O(KV) K爲優先隊列解壓最小的實現
  • 優化:優化堆的實現(不嫌麻煩就手打FIB堆。。優先隊列是用二叉堆的。)

//好像漏洞百出,以後再來更新,歡迎大家幫忙找漏洞,歡迎吐槽

發佈了40 篇原創文章 · 獲贊 2 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章