1. 程式人生 > >【圖(下)】最小生成樹問題

【圖(下)】最小生成樹問題

1、什麼是最小生成樹(Minimum Spanning Tree)

  • 是一棵
    • 無迴路
    • |V|個頂點一定有|V|-1條邊
  • 生成
    • 包含全部頂點
    • |V|-1條邊都在圖裡
  • 邊的權重和最小
    在這裡插入圖片描述
    最小生成樹存在↔ 圖連通

2、貪心演算法

  • 什麼是“貪”:每一步都要最好的
  • 什麼是“好”:權重最小的邊
  • 需要約束:
    • 只能用圖裡有的邊
    • 只能正好用掉|V|-1條邊
    • 不能有迴路

3、Prim演算法— 讓一棵小樹長大

在這裡插入圖片描述

void Prim()
{
	MST = { s };//s維護折樹中所有的頂點
	while (1)
	{
	//dist值的含義為距離這棵樹的距離,樹節點dist為0;
	//和樹相鄰節點dist為邊權重,不相鄰節點dist為無窮大。
		V = 未收錄頂點中dist最小者;
		if (這樣的V不存在)
			break;
	將V收錄進MST: dist[V] = 0;//dist設為0表示收入樹中
		for (V 的每個鄰接點W)//收錄一個頂點會對該定點相鄰頂點dist有影響
			if (W未被收錄)
				if (E(V, W) < dist[W])
				{
					dist[
W] = E(V, W); parent[W] = V; } } if (MST中收的頂點不到 | V |)//圖不連通 Error(“生成樹不存在”); }

Prim演算法中的dist[V]初始化:
dist[V] = E(s,V)或正無窮

parent[s] = -1
時間複雜度:
T = O( |V|2 ) 稠密圖合算

3、Kruskal演算法— 將森林合併成樹

圖中每一個頂點都可以看成一棵樹,每次找權重最小的邊,把它收進來。每次把邊收進來,就把兩棵樹併成一棵樹,最後把所有的節點併成一棵樹。
在這裡插入圖片描述

void Kruskal(Graph G)
{
MST = {};//生成樹集合中收集的元素為邊 while (MST 中不到 |V| -1 條邊&& E 中還有邊) { 從E中取一條權重最小的邊E(v, w);/* 最小堆*/E(v, w)從E中刪除; if (E(V, W)不在MST中構成迴路)/* 並查集*/E(V, W) 加入MST; else 徹底無視E(V, W);//生成樹不要該邊,E中也不要 } if (MST 中不到 |V| -1 條邊)//圖不連通 Error(“生成樹不存在”); }
  1. 從E中取一個權重最小的邊,將E中元素組織成一個最小堆,每次取出一條邊複雜度為log(E)。

  2. 判斷E(v,w)(表示從頂點V到頂點W的一條邊)的加入會不會在最小生成樹中構成迴路:
    使用並查集,加邊E(v,w)之前檢查頂點V和W,如果二者屬於不同的樹,不會構成迴路,在同一棵樹中則會構成迴路。

時間複雜度:
T = O( |E| log |E| )
對於稀疏圖比較划算