贪心算法----最小耗费生成树

在一给定的无向图G = (V, E) 中,(u, v) 代表连接顶点 u 与顶点 v 的边(即),而 w(u, v) 代表此的权重,若存在 T 为 E 的子集(即)且为无循环图,使得

的 w(T) 最小,则此 T 为 G 的最小生成树

最小生成树其实是最小权重生成树的简称

1、Kruskal算法:

主要步骤:1.对G的边以非降序权重排列;

                  2.对于排序表中的每条边,如果现在把它放入T不会形成回路的话,则把它加入到生成树T中;否则把它丢弃。

伪代码如下:Kruskal

输入:包含n个顶点的含权连通无向图G=(V,E)

输出:由G生成的最小耗费生成树T组成的边的集合

按非降序权重将E中的边排序

for 每条边 v∈V

    MAKESET({v})

end for

T={}

while ∣T∣<n-1

    令(x,y)为E中的下一条边

    if FIND(x) ≠ FIND(y) then

        将(x,y)加入T

        UNION(x,y)

    end if

end while

此算法时间复杂度为:Ο(mlogm)

详细代码如下:

#include<iostream>

#include<vector>

#include<algorithm>

using namespace std;

const int maxn = 1e4 + 5;

int father[maxn];

struct node

{

    int u, v, val;

}edge[maxn];

bool cmp(node&a, node&b)

{

    return a.val < b.val;

}

int Find(int x)

{

    if (x == father[x])

        return father[x];

    else 

        return father[x] = Find(father[x]);

}

void join(int x, int y)

{

    int fa = Find(x);

    int fb = Find(y);

    if (fa != fb) 

        father[fb] = fa;

}

int kruskal(int n, int m)//n个点,m条边的完全连通图的克鲁斯卡尔算法

{

    sort(edge, edge + m, cmp);

    //初始化并查集

    for (int i = 0; i <=n; i++)

        father[i] = i;

    int ans = 0;

    for (int i = 0; i < m; i++)

    {

        int fa = Find(edge[i].v);

        int fb = Find(edge[i].u);

        if (fa != fb)

        {

            join(fa, fb);

            ans += edge[i].val;

        }

    }

    return ans;

}

int main()

{

    int n;

    while (cin >> n && n)

    {

        int m = (n - 1)*n / 2;

        for (int i = 0; i < m; i++)

        {

            cin >> edge[i].v >> edge[i].u >> edge[i].val;

        }

        cout << kruskal(n, m) << endl;

    }

}

参考:最小生成树——kruskal算法 - YG爱木木 - CSDN博客

2、Prim算法

算法描述:

1).输入:一个加权连通图,其中顶点集合为V,边集合为E;

2).初始化:Vnew = {x},其中x为集合V中的任一节点(起始点),Enew = {},为空;

3).重复下列操作,直到Vnew = V:

    a.在集合E中选取权值最小的边,其中u为集合Vnew中的元素,而v不在Vnew集合当中,并且v∈V(如果存在有多条满足前述条件即具有相同权值的边,则可任意选取其中之一);

    b.将v加入集合Vnew中,将边加入集合Enew中;

4).输出:使用集合Vnew和Enew来描述所得到的最小生成树

伪代码如下:Prim

输入:包含n个顶点的含权连通无向图G=(V,E)

输出:由G生成的最小耗费生成树T组成的边的集合 

T={};    X←{1};    Y←V﹣{1}

for y←2 to n

    if y邻接于1 then

        N[y]← 1

        C[y]← c[1,y]

    else

        C[y]←∞

    end if

end for

for j← 1 to n-1    {寻找n-1条边}

    令y∈Y,使得C[y]最小

    T←T∪{(y,N[y])}    {将边(y,N[y])加入T}

    X←X∪{y}    {将顶点y加入X}

    Y←Y-{y}    {从Y删除顶点y}

    for 每个邻接于y的顶点w∈Y

        if c[y,w]<C[w] then

            N[w]←y

            C[w]←c[y,w]

        end if

    end for

end for

此算法时间复杂度为:Ο(n²)

详细代码如下:

// Prim最小生成树

void Prim(int nStart)

{

    int i = 0;

    int nIndex=0;        // prim最小树的索引,即prims数组的索引

    char cPrims[MAX];    // prim最小树的结果数组

    int weights[MAX];    // 顶点间边的权值


    cPrims[nIndex++] = m_mVexs[nStart].data;

    // 初始化"顶点的权值数组",

    // 将每个顶点的权值初始化为"第start个顶点"到"该顶点"的权值。

    for (i = 0; i < m_nVexNum; i++)

    {

        weights[i] = GetWeight(nStart, i);

    }

    for (i = 0; i < m_nVexNum; i ++)

    {

        if (nStart == i)

        {

            continue;

        }


        int min = INF;

        int nMinWeightIndex = 0;

        for (int k = 0; k < m_nVexNum; k ++)

        {

            if (weights[k]!= 0 && weights[k] < min)

            {

                min = weights[k];

                nMinWeightIndex = k;

            }

        }

        // 找到下一个最小权重值索引

        cPrims[nIndex++] = m_mVexs[nMinWeightIndex].data;

        // 以找到的顶点更新其他点到该点的权重值

        weights[nMinWeightIndex]=0;

        int nNewWeight = 0;

        for (int ii = 0; ii < m_nVexNum; ii++)

        {

            nNewWeight = GetWeight(nMinWeightIndex, ii);

            // 该位置需要特别注意

            if (0 != weights[ii] && weights[ii] > nNewWeight)

            {

                weights[ii] = nNewWeight;

            }

        }

    }

    // 计算最小生成树的权重值

    int nSum = 0;

    for (i = 1; i < nIndex; i ++)

    {

        int min = INF;

        int nVexsIndex = GetVIndex(cPrims[i]);

        for (int kk = 0; kk < i; kk ++)

        {

            int nNextVexsIndex = GetVIndex(cPrims[kk]);

            int nWeight = GetWeight(nVexsIndex, nNextVexsIndex);

            if (nWeight < min)

            {

                min = nWeight;

            }

        }

        nSum += min;

    }

    // 打印最小生成树

    cout << "PRIM(" << m_mVexs[nStart].data  <<")=" << nSum << ": ";

    for (i = 0; i < nIndex; i++)

        cout << cPrims[i] << " ";

    cout << endl;

}


参考:数据结构之最小生成树Prim算法 - Fate0729 - 博客园

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,386评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,142评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,704评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,702评论 1 294
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,716评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,573评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,314评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,230评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,680评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,873评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,991评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,706评论 5 346
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,329评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,910评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,038评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,158评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,941评论 2 355

推荐阅读更多精彩内容