最小生成树(Prim算法和Kruskal算法)

1)最小生成树

给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫生成树。如果边上有权值,那么使得边权和最小的生成树叫做最小生成树(MST,Minimum Spanning Tree)

2)应用

比如让你为一个镇的九个村庄架设通信网络,每个村庄相当于一个顶点,权值是村与村之间可通达的直线距离,要求你必须用最小的成本完成这次任务;或者村庄之间建公路,连通N个村庄要N-1条路,如何让建路成本最低之类的问题。

1、Prim算法

①该算法是构建最小生成树的算法之一。它是以某顶点为起点,不断添加各顶点上最小权值的边,来构建最小生成树。

②算法流程:

第一步:设图G顶点集合为U,首先任意选择图G中的一点作为起始点a,将该点加入集合V;

第二步:从集合U中找到另一点b使得点b到V中任意一点的权值最小,此时将b点也加入集合V;

以此类推,现在的集合V={a,b},再从集合U中找到另一点c使得点c到V中任意一点的权值最小,此时将c点加入集合V;直至所有顶点全部被加入V,此时就构建出了一棵MST。

③参考代码:

#include <iostream>
#include <string.h>
#define INT_MAX 1000000000
using namespace std;

int matrix[100][100];//邻接矩阵
bool visited[100];//标记数组
int path[100];//记录生成树路径
int lowcost[100];//边的权值
int vertex_num,arc_num;//顶点数,弧数
int sum;//权值总和
int source;//源点
void prim(int source);
int main()
{
    cout << "请输入图的顶点数(<=100):";
    cin >> vertex_num;
    cout << "请输入图的弧数:";
    cin >> arc_num;

    for (int i = 0; i < vertex_num; i++)
        for (int j = 0; j < vertex_num; j++)
            matrix[i][j] = INT_MAX;  //初始化matrix数组

    cout << "请输入弧的信息:\n";
    int u, v, w;
    for (int i = 0; i < arc_num; i++)
    {
        cin >> u >> v >> w;
        matrix[u][v] = matrix[v][u] = w;
    }

    cout << "请输入起点(<" << vertex_num << "):";
    cin >> source;
    prim(source);

    cout << "最小生成树权和为:" << sum << endl;
    cout << "最小生成树路径为:\n";
    for (int i = 0; i < vertex_num; i++)
        if (i != source)
            cout << i << "----" << path[i] << endl;

    return 0;
}
void prim(int source){
    memset(visited,0,sizeof(visited));
    visited[source]=true;
    for(int i=0;i<vertex_num;i++){
        lowcost[i] = matrix[source][i];
        path[i]=source;
    }

    int min_cost,min_cost_index;//最小的权值,和其下标
    sum=0;
    for(int i=1;i<vertex_num;i++){//找除源点外的n-1个点,如果这里写多了,
                                //那么下边的for里if进不去,那么sum的值也会错误
            min_cost=INT_MAX;

        for(int j=0;j<vertex_num;j++){//遍历所有顶点
            if(visited[j]==false&&lowcost[j]<min_cost){//找到与源点的权值最小的点
                min_cost=lowcost[j];
                min_cost_index=j;
            }
        }
        visited[min_cost_index]=true;//讲找到的顶点进行标记
        sum+=min_cost;//权值总和

        for(int j=0;j<vertex_num;j++){
            if(visited[j]==false&&matrix[min_cost_index][j]<lowcost[j]){//更新lowcost,以便找下个顶点
                lowcost[j]=matrix[min_cost_index][j];
                path[j] = min_cost_index;
            }
        }
    }

}

摘自http://www.61mon.com/index.php/archives/199/comment-page-1#comments

2、Kruskal算法

①该算法是构建最小生成树的另一个算法,其是对边进行操作,来构建最小生成树的。

②算法流程:

第一步:把所有的边按权值从小到大排列。

第二步:按照顺序选取每条边,如果这条边的两个端点不属于同一集合,那么就将它们合并。

重复第二步,直到 所有的点都属于同一个集合。     

第二步用并查集来实现,又因为每次都选的权值最小的,所以这其实就是运用并查集的贪心算法。

③为什么这样做就可以构建最小生成树呢?因为N个顶点只需要N-1条边就够了,那么选哪N-1条呢,为了让权值和最小,当然选从小到大排序的前N-1条边喽。

④参考代码:

#include <iostream>
#include <algorithm>
#define MAX_INT 100
using namespace std;
struct Edge{//边
int a;//边上的两个点
int b;
int weight;//权值
}edge[MAX_INT];
int par[MAX_INT];//i的父亲的编号
int rank[MAX_INT];//i的高度
void init(int n);//初始化
int find(int x);//查找根节点并且压缩路径
bool  unite(int x,int y);//合并
bool compare(const Edge&a,const Edge&b);
int vertex_num,arc_num;//顶点数,弧数
int case_num;//测试组数
int sum;//权值总和
int main()
{
    cout<<"请输入测试组数:"<<endl;
    cin>>case_num;
    while(case_num){
        sum=0;
        cout<<"请输入顶点数和弧数:"<<endl;
        cin>>vertex_num>>arc_num;

        init(vertex_num);

        cout<<"请输入"<<arc_num<<"条弧的信息:"<<endl;
        for(int i=0;i<arc_num;i++){
            cin>>edge[i].a>>edge[i].b>>edge[i].weight;
            }

        sort(edge,edge+arc_num,compare);

        cout<<"最小生成树的路径为:"<<endl;
        int j;
        for(j=0;j<arc_num;j++){
            if(unite(edge[j].a,edge[j].b)){
                sum+=edge[j].weight;
                cout<<edge[j].a<<"----"<<edge[j].b<<" "<<edge[j].weight<<endl;
        }
        }
        if(j==arc_num){
            cout<<"最小生成树的权值和为:"<<sum<<endl;
        }else if(j<arc_num){
            cout<<"data error!"<<endl;
        }

        case_num--;
    }
    return 0;
}
void init(int n){
    for(int i=0;i<n;i++){
        par[i]=i;//父节点都先初始化为自己
        rank[i]=0;//高度初始化为0
    }

}

int find(int x){
if(x==par[x]){
    return x;
}else{
    return par[x]=find(par[x]);
}
}
bool  unite(int x,int y){
    x=find(x);
    y=find(y);
    if(x==y){
        return false;
    }else{
    if(rank[x]<rank[y]){
        par[x]=y;
    }else{
        par[y]=x;
        if(rank[x]==rank[y])
            rank[x]++;
    }
    return true;
    }

}

bool compare(const Edge&a,const Edge&b){//按从小到大的顺序排序
    return a.weight<b.weight;
}

代码参考自:http://blog.csdn.net/niushuai666/article/details/6689285

3、Kruskal算法和Prim算法的区别(或者说什么时候用Prim什么时候用Kruskal)?

关于这个问题,我也不是很明白,后续会补充,欢迎在评论区写下自己对此的理解,一起来讨论下吧??

时间: 2024-10-06 03:54:56

最小生成树(Prim算法和Kruskal算法)的相关文章

转载:最小生成树-Prim算法和Kruskal算法

本文摘自:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 最小生成树-Prim算法和Kruskal算法 Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:

最小生成树Prim算法和Kruskal算法

Prim算法(使用visited数组实现) Prim算法求最小生成树的时候和边数无关,和顶点树有关,所以适合求解稠密网的最小生成树. Prim算法的步骤包括: 1. 将一个图分为两部分,一部分归为点集U,一部分归为点集V,U的初始集合为{V1},V的初始集合为{ALL-V1}. 2. 针对U开始找U中各节点的所有关联的边的权值最小的那个,然后将关联的节点Vi加入到U中,并且从V中删除(注意不能形成环). 3. 递归执行步骤2,直到V中的集合为空. 4. U中所有节点构成的树就是最小生成树. 方法

最小生成树-Prim算法和Kruskal算法

原文链接:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vojtěch Jarník)发现:并在195

[转载]最小生成树-Prim算法和Kruskal算法

转载地址:http://www.cnblogs.com/biyeymyhjob/archive/2012/07/30/2615542.html 自己在学,感觉这个讲的很不错,就转载了. Prim算法 1.概览 普里姆算法(Prim算法),图论中的一种算法,可在加权连通图里搜索最小生成树.意即由此算法搜索到的边子集所构成的树中,不但包括了连通图里的所有顶点(英语:Vertex (graph theory)),且其所有边的权值之和亦为最小.该算法于1930年由捷克数学家沃伊捷赫·亚尔尼克(英语:Vo

最小生成树之 prim算法和kruskal算法(以 hdu 1863为例)

最小生成树的性质 MST性质:设G = (V,E)是连通带权图,U是V的真子集.如果(u,v)∈E,且u∈U,v∈V-U,且在所有这样的边中, (u,v)的权c[u][v]最小,那么一定存在G的一棵最小生成树,(u,v)为其中一条边. 构造最小生成树,要解决以下两个问题: (1).尽可能选取权值小的边,但不能构成回路(也就是环). (2).选取n-1条恰当的边以连接网的n个顶点. Prim算法的思想: 设G = (V,E)是连通带权图,V = {1,2,-,n}.先任选一点(一般选第一个点),首

最小生成树:prim算法和kruskal算法

一个连通图的生成树是图的极小连通子图.它包含图中的所有顶点,并且只含尽可能少的边.若砍去它的一条边,就会使生成树变成非连通图:若给它增加一条边,则会形成一条回路. 最小生成树有如下性质: 1.最小生成树非唯一,可能有多个最小生成树: 2.最小生成树的边的权值之和总唯一,而且是最小的: 3.最小生成树的边数为顶点数减1. 构造最小生成树可以有多种算法.其中多数算法利用了最小生成树的下列一种简称为MST的性质: 假设N=(V,{E})是一个连通网,U是顶点集V的一个非空子集.若(u, v)是一条具有

java实现最小生成树的prim算法和kruskal算法

在边赋权图中,权值总和最小的生成树称为最小生成树.构造最小生成树有两种算法,分别是prim算法和kruskal算法.在边赋权图中,如下图所示: 在上述赋权图中,可以看到图的顶点编号和顶点之间邻接边的权值,若要以上图来构建最小生成树.结果应该如下所示: 这样构建的最小生成树的权值总和最小,为17 在构建最小生成树中,一般有两种算法,prim算法和kruskal算法 在prim算法中,通过加入最小邻接边的方法来建立最小生成树算法.首先构造一个零图,在选一个初始顶点加入到新集合中,然后分别在原先的顶点

Prim算法和Kruskal算法的正确性证明

今天学习了Prim算法和Kruskal算法,因为书中只给出了算法的实现,而没有给出关于算法正确性的证明,所以尝试着给出了自己的证明.刚才看了一下<算法>一书中的相关章节,使用了切分定理来证明这两个算法的正确性,更加简洁.优雅并且根本.相比之下,我的证明带着许多草莽气息,于此写成博客,只当是记录自己的思考 ------------------------------------------- 说明: 本文仅提供关于两个算法的正确性的证明,不涉及对算法的过程描述和实现细节 本人算法菜鸟一枚,提供的

Prim算法和Kruskal算法求最小生成树

Prim算法 连通分量是指图的一个子图,子图中任意两个顶点之间都是可达的.最小生成树是连通图的一个连通分量,且所有边的权值和最小. 最小生成树中,一个顶点最多与两个顶点邻接:若连通图有n个顶点,则最小生成树中一定有n-1条边. Prim算法需要两个线性表来进行辅助: visited: 标记已经加入生成树的顶点:(它的功能可以由tree取代) 初始状态:生成树根节点为真,其它为0. tree: 记录生成树,tree[x]保存顶点x的直接根节点下标,若x为树的根节点则tree[x]为其自身. 初始状