權重最小生成樹的思想與Kruskal算法

本文有更新,請移步個人我的博客:https://blog.andyqiao.top/article/9/算法

 

    晚上作攜程的筆試題,附加題考到了權重最小生成樹。OMG,就在開考以前,我還又看過一遍這內容,可由於時間太緊,也歷來沒有寫過代碼,就GG了。又吃了眼高手低的虧。這不,就好好總結一下,亡羊補牢。安全

    權重最小生成樹問題是指在一棵無向全鏈接圖中找到一個無環子集T,既能將全部的結點鏈接起來,又具備最小的權重和。函數

    解決問題的核心是每次找到一條安全邊加入到邊集合A中,使得A仍然是某棵最小生成樹的子集。spa

    Kruskal找到安全邊的方法是:在全部鏈接森林中兩棵不一樣樹的邊裏面,找到權重最小的邊(u,v),(1)若是u和v位於不一樣的子樹,則該邊就是一個安全邊,將u和v位於的子樹合併起來;(2)若是u和v位於相同子樹,則該邊不是一個安全邊,若是將兩棵子樹鏈接起來,就會造成一個環。code

    我用詳細註釋的代碼說明問題:blog

//合併節點a和b所屬的兩棵子樹
void Union(int a, int b, int V,vector<int>& root)
{
    int root_a = root[a],root_b = root[b];
    //把b所在樹的全部頂點都移植過去給a...  
        
    for (int i = 0; i < V; i++)
        if (root[i] == root_b)
            root[i] = root_a;
}
//sort的比較函數
bool compare(const CEdge &a, const CEdge &b)
{
    return  a.weight < b.weight;
}
//Kruskal最小生成樹算法
void Kruskal(int V, int E, vector<CEdge> &e,vector<int>& root)
{
    //以權重爲參考值,排序全部邊
    sort(e.begin(), e.end(), compare);
    int cnt = 0;
    for (int i = 0; i < E; i++)
        if (root[e[i].u]!=root[e[i].v]) //若是e[i].u和e[i].v不屬於同一棵子樹
        {
            cout << e[i].u << "---" << e[i].v << " "<<e[i].weight<<endl;//加入該邊
            Union(e[i].u, e[i].v, V,root);           //合併兩棵子樹

            //易知最小生成樹擁有V-1條邊
            //若是已經組成最小生成樹,就退出循環
            ++cnt;
            if (cnt >= V - 1)
                break;
        }
}
int main()
{
    int V = 4;
    vector<CEdge> edges;
    edges.push_back({ 0, 1, 1 });
    edges.push_back({ 0, 2, 2 });
    edges.push_back({ 0, 3, 3 });
    edges.push_back({ 1, 2, 4 });
    edges.push_back({ 1, 3, 5 });
    edges.push_back({ 2, 3, 2 });
    
    //使用一個vector來表示各個子樹(即集合A),
    //root[i]=j,表示節點i與節點j位於同一子樹上,而且全部位於此樹的節點k,都有root[k]=j;
    //初始化root[i]=i,表示每棵子樹只是一個節點
    vector<int> root(V, 0);
    for (int i = 0; i < V; ++i)
        root[i] = i;

    //執行算法
    Kruskal(V, edges.size(), edges, root);

    while (1);
    return 0;
}

程序輸出:排序

0---1 1
0---2 2
2---3 2get

相關文章
相關標籤/搜索