1. 程式人生 > >LeetCode基礎-圖-最小生成樹

LeetCode基礎-圖-最小生成樹

加權圖:為每條邊關聯一個權值或者說成本的圖模型。
圖的生成樹:含有圖的所有頂點的無環連通子圖。
最小生成樹:加權無向圖的最小生成樹(MST)是它的一棵權值最小(所有邊的權值之和)的生成樹。
下圖是加權無向圖與它的最小生成樹。

這裡寫圖片描述

最小生成樹有兩個經典演算法:

  • Prim 演算法
  • Kruskal 演算法

如果一幅圖是非連通的,則只能用這個演算法計算所有連通分量的最小生成樹,合併在一起叫做最小生成森林

還有幾點要注意的:

  • 邊的權重未必是距離。
  • 邊的權重可能小於等於 0 。
  • 所有邊的權重都可能相同也可能不相同。

兩個性質:

  • 用一條邊連線樹中的任意兩個頂點都會產生一個新的環。
    這裡寫圖片描述
  • 從樹中刪去一條邊可以得到兩棵獨立的樹。
    這裡寫圖片描述

圖的一種切分是把圖的所有頂點分為兩個 非空 且 不重複 的集合。橫切片是一條連線兩個屬於不同集合的邊。

通常,我們指定一個頂點集,然後隱式地認為它的補集是另一個頂點集。
給定任意的切分,它的橫切邊中的權重最小者必然屬於圖的最小生成樹。
假設所有邊的權重不相同,則每幅連通圖都只有唯一的最小生成樹。

貪心演算法得到最小生成樹:

這裡寫圖片描述

帶權重的邊的資料型別:

public class Edge implements Comparable<Edge>
{
    private final int v; // one vertex
    private
final int w; // the other vertex private final double weight; // edge weight public Edge(int v, int w, double weight) { this.v = v; this.w = w; this.weight = weight; } public double weight() { return weight; } public int either() { return v; } public
int other(int vertex) { if (vertex == v) { return w;} else if (vertex == w) { return v;} else { throw new RuntimeException("Inconsistent edge");} } public int compareTo(Edge that) { if (this.weight() < that.weight()) { return -1; } else if (this.weight() > that.weight()) { return +1; } else { return 0; } } public String toString() { return String.format("%d-%d %.2f", v, w, weight); } }

加權無向圖的資料型別:

“`
public class EdgeWeightedGraph
{
private final int V; // number of vertices
private int E; // number of edges
private Bag[] adj; // adjacency lists

public EdgeWeightedGraph(int V)
{
    this.V = V;
    this.E = 0;
    adj = (Bag<Edge>[]) new Bag[V];
    for (int v = 0; v < V; v++)
    {
        adj[v] = new Bag<Edge>();
    }
}
public int V() { return V; }
public int E() { return E; }

public void addEdge(Edge e)
{
    int v = e.either(), w = e.other(v);
    adj[v].add(e);
    adj[w].add(e);
    E++;
}
public Iterable<Edge> adj(int v)
{ return adj[v]; }
public Iterable<Edge> edges()
{
    Bag<Edge> list = new Bag<Edge>();
    for (int v = 0; v < V; v++) 
    {
        int selfLoops = 0;
        foreach (Edge e in adj(v)) 
        {
            if (e.other(v) > v) 
            {
                list.add(e);
            }
            // only add one copy of each self loop (self loops will be consecutive)
            else if (e.other(v) == v) 
            {
                if (selfLoops % 2 == 0) list.add(e);
                selfLoops++;
            }
        }
    }
    return list;
}

}

Prim演算法

Prim演算法能夠得到任意加權無向圖的最小生成樹。
每一步都會為成長中的樹加一條邊。

Lazy實現:

這裡寫圖片描述

Eager實現:

這裡寫圖片描述

Kruskal 演算法

Kruskal 演算法的思想是按照邊的權重順序(從小到大)加入到樹中,加入的邊不會構成環。

這裡寫圖片描述